agents-city 0.3.0-beta.21

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.
Files changed (440) hide show
  1. package/.claude-plugin/marketplace.json +16 -0
  2. package/LICENSE +202 -0
  3. package/NOTICE +5 -0
  4. package/README.es.md +2021 -0
  5. package/README.md +1999 -0
  6. package/benchmarks/committee/README.md +39 -0
  7. package/benchmarks/committee/metrics.py +51 -0
  8. package/benchmarks/committee/run.py +64 -0
  9. package/benchmarks/committee/traces/01-single.json +8 -0
  10. package/benchmarks/committee/traces/02-mesh.json +16 -0
  11. package/benchmarks/committee/traces/03-chair.json +15 -0
  12. package/benchmarks/latency/README.md +35 -0
  13. package/benchmarks/latency/fake-claude-cli.mjs +119 -0
  14. package/benchmarks/latency/fake-native-server.mjs +340 -0
  15. package/benchmarks/latency/fake-tui.py +91 -0
  16. package/benchmarks/latency/live.py +380 -0
  17. package/benchmarks/stress/README.md +20 -0
  18. package/benchmarks/stress/run.py +487 -0
  19. package/bin/agents +4 -0
  20. package/bin/agents-city.js +116 -0
  21. package/bin/benchmark +17 -0
  22. package/bin/bus +6 -0
  23. package/bin/cities +3 -0
  24. package/bin/city +86 -0
  25. package/bin/committee +6 -0
  26. package/bin/demo +200 -0
  27. package/bin/exit +5 -0
  28. package/bin/hall +73 -0
  29. package/bin/hall.html +426 -0
  30. package/bin/logs +3 -0
  31. package/bin/report.py +14 -0
  32. package/bin/reset +3 -0
  33. package/bin/road +3 -0
  34. package/bin/seat +10 -0
  35. package/bin/serve.py +1428 -0
  36. package/bin/setup.py +753 -0
  37. package/bin/skills +3 -0
  38. package/bin/test +131 -0
  39. package/bin/test-adapter.py +183 -0
  40. package/bin/test-admision.py +99 -0
  41. package/bin/test-avatar.py +58 -0
  42. package/bin/test-benchmark.py +40 -0
  43. package/bin/test-broker.py +334 -0
  44. package/bin/test-cage.py +206 -0
  45. package/bin/test-card.py +402 -0
  46. package/bin/test-channel.py +379 -0
  47. package/bin/test-cities.py +453 -0
  48. package/bin/test-claude-runtime.py +252 -0
  49. package/bin/test-committee.py +340 -0
  50. package/bin/test-contracts.py +544 -0
  51. package/bin/test-crecimiento.py +164 -0
  52. package/bin/test-demo.py +308 -0
  53. package/bin/test-doctor.py +106 -0
  54. package/bin/test-domains.py +151 -0
  55. package/bin/test-evidencia.py +50 -0
  56. package/bin/test-exit.py +128 -0
  57. package/bin/test-hall-protocol.py +93 -0
  58. package/bin/test-launch.py +169 -0
  59. package/bin/test-live-feed.py +508 -0
  60. package/bin/test-pairing.py +99 -0
  61. package/bin/test-parcels.py +283 -0
  62. package/bin/test-runtime-failures.py +252 -0
  63. package/bin/test-runtime-ui.py +488 -0
  64. package/bin/test-runtime.py +268 -0
  65. package/bin/test-rutas.py +80 -0
  66. package/bin/test-seat.py +1386 -0
  67. package/bin/test-security.py +150 -0
  68. package/bin/test-serve.py +1057 -0
  69. package/bin/test-stress.py +64 -0
  70. package/bin/test-widgets.py +143 -0
  71. package/bin/test-workspace.py +188 -0
  72. package/bin/testlib.py +95 -0
  73. package/bin/tokens.py +14 -0
  74. package/bus/scripts/deploy.sh +60 -0
  75. package/bus/scripts/mint-token.sh +68 -0
  76. package/bus/scripts/setup-dev.sh +79 -0
  77. package/bus/scripts/test-channel.ts +8 -0
  78. package/bus/scripts/test-hub.ts +266 -0
  79. package/bus/scripts/test-local.sh +40 -0
  80. package/bus/scripts/test-queue.ts +132 -0
  81. package/bus/worker/package-lock.json +1971 -0
  82. package/bus/worker/package.json +17 -0
  83. package/bus/worker/src/index.ts +485 -0
  84. package/bus/worker/tsconfig.json +13 -0
  85. package/bus/worker/wrangler.toml +32 -0
  86. package/city/oven/README.md +59 -0
  87. package/city/oven/collect.py +43 -0
  88. package/city/oven/oven.html +4 -0
  89. package/city/scripts/history.py +120 -0
  90. package/city/scripts/seed.py +442 -0
  91. package/city/web/assets/modelos/License.txt +28 -0
  92. package/city/web/assets/modelos/PROCEDENCIA.txt +3 -0
  93. package/city/web/assets/modelos/building-a.glb +0 -0
  94. package/city/web/assets/modelos/building-b.glb +0 -0
  95. package/city/web/assets/modelos/building-c.glb +0 -0
  96. package/city/web/assets/modelos/building-d.glb +0 -0
  97. package/city/web/assets/modelos/building-e.glb +0 -0
  98. package/city/web/assets/modelos/building-f.glb +0 -0
  99. package/city/web/assets/modelos/building-g.glb +0 -0
  100. package/city/web/assets/modelos/building-h.glb +0 -0
  101. package/city/web/assets/sprites/building-a.png +0 -0
  102. package/city/web/assets/sprites/building-b.png +0 -0
  103. package/city/web/assets/sprites/building-c.png +0 -0
  104. package/city/web/assets/sprites/building-d.png +0 -0
  105. package/city/web/assets/sprites/building-e.png +0 -0
  106. package/city/web/assets/sprites/building-f.png +0 -0
  107. package/city/web/assets/sprites/building-g.png +0 -0
  108. package/city/web/assets/sprites/building-h.png +0 -0
  109. package/city/web/assets/sprites/building-i.png +0 -0
  110. package/city/web/assets/sprites/building-j.png +0 -0
  111. package/city/web/assets/sprites/building-k.png +0 -0
  112. package/city/web/assets/sprites/building-l.png +0 -0
  113. package/city/web/assets/sprites/building-m.png +0 -0
  114. package/city/web/assets/sprites/building-n.png +0 -0
  115. package/city/web/assets/sprites/building-skyscraper-a.png +0 -0
  116. package/city/web/assets/sprites/building-skyscraper-b.png +0 -0
  117. package/city/web/assets/sprites/building-skyscraper-c.png +0 -0
  118. package/city/web/assets/sprites/building-skyscraper-d.png +0 -0
  119. package/city/web/assets/sprites/building-skyscraper-e.png +0 -0
  120. package/city/web/assets/sprites/catalogo.json +47 -0
  121. package/city/web/assets/sprites/detail-awning-wide.png +0 -0
  122. package/city/web/assets/sprites/detail-awning.png +0 -0
  123. package/city/web/assets/sprites/detail-overhang-wide.png +0 -0
  124. package/city/web/assets/sprites/detail-overhang.png +0 -0
  125. package/city/web/assets/sprites/detail-parasol-a.png +0 -0
  126. package/city/web/assets/sprites/detail-parasol-b.png +0 -0
  127. package/city/web/assets/sprites/low-detail-building-a.png +0 -0
  128. package/city/web/assets/sprites/low-detail-building-b.png +0 -0
  129. package/city/web/assets/sprites/low-detail-building-c.png +0 -0
  130. package/city/web/assets/sprites/low-detail-building-d.png +0 -0
  131. package/city/web/assets/sprites/low-detail-building-e.png +0 -0
  132. package/city/web/assets/sprites/low-detail-building-f.png +0 -0
  133. package/city/web/assets/sprites/low-detail-building-g.png +0 -0
  134. package/city/web/assets/sprites/low-detail-building-h.png +0 -0
  135. package/city/web/assets/sprites/low-detail-building-i.png +0 -0
  136. package/city/web/assets/sprites/low-detail-building-j.png +0 -0
  137. package/city/web/assets/sprites/low-detail-building-k.png +0 -0
  138. package/city/web/assets/sprites/low-detail-building-l.png +0 -0
  139. package/city/web/assets/sprites/low-detail-building-m.png +0 -0
  140. package/city/web/assets/sprites/low-detail-building-n.png +0 -0
  141. package/city/web/assets/sprites/low-detail-building-wide-a.png +0 -0
  142. package/city/web/assets/sprites/low-detail-building-wide-b.png +0 -0
  143. package/city/web/assets/sprites/medidas.json +248 -0
  144. package/city/web/dist/city.js +939 -0
  145. package/city/web/dist/index.html +294 -0
  146. package/city/web/dist/modelos/License.txt +28 -0
  147. package/city/web/dist/modelos/PROCEDENCIA.txt +3 -0
  148. package/city/web/dist/modelos/building-a.glb +0 -0
  149. package/city/web/dist/modelos/building-b.glb +0 -0
  150. package/city/web/dist/modelos/building-c.glb +0 -0
  151. package/city/web/dist/modelos/building-d.glb +0 -0
  152. package/city/web/dist/modelos/building-e.glb +0 -0
  153. package/city/web/dist/modelos/building-f.glb +0 -0
  154. package/city/web/dist/modelos/building-g.glb +0 -0
  155. package/city/web/dist/modelos/building-h.glb +0 -0
  156. package/city/web/dist/sprites/building-a.png +0 -0
  157. package/city/web/dist/sprites/building-b.png +0 -0
  158. package/city/web/dist/sprites/building-c.png +0 -0
  159. package/city/web/dist/sprites/building-d.png +0 -0
  160. package/city/web/dist/sprites/building-e.png +0 -0
  161. package/city/web/dist/sprites/building-f.png +0 -0
  162. package/city/web/dist/sprites/building-g.png +0 -0
  163. package/city/web/dist/sprites/building-h.png +0 -0
  164. package/city/web/dist/sprites/building-i.png +0 -0
  165. package/city/web/dist/sprites/building-j.png +0 -0
  166. package/city/web/dist/sprites/building-k.png +0 -0
  167. package/city/web/dist/sprites/building-l.png +0 -0
  168. package/city/web/dist/sprites/building-m.png +0 -0
  169. package/city/web/dist/sprites/building-n.png +0 -0
  170. package/city/web/dist/sprites/building-skyscraper-a.png +0 -0
  171. package/city/web/dist/sprites/building-skyscraper-b.png +0 -0
  172. package/city/web/dist/sprites/building-skyscraper-c.png +0 -0
  173. package/city/web/dist/sprites/building-skyscraper-d.png +0 -0
  174. package/city/web/dist/sprites/building-skyscraper-e.png +0 -0
  175. package/city/web/dist/sprites/catalogo.json +47 -0
  176. package/city/web/dist/sprites/detail-awning-wide.png +0 -0
  177. package/city/web/dist/sprites/detail-awning.png +0 -0
  178. package/city/web/dist/sprites/detail-overhang-wide.png +0 -0
  179. package/city/web/dist/sprites/detail-overhang.png +0 -0
  180. package/city/web/dist/sprites/detail-parasol-a.png +0 -0
  181. package/city/web/dist/sprites/detail-parasol-b.png +0 -0
  182. package/city/web/dist/sprites/low-detail-building-a.png +0 -0
  183. package/city/web/dist/sprites/low-detail-building-b.png +0 -0
  184. package/city/web/dist/sprites/low-detail-building-c.png +0 -0
  185. package/city/web/dist/sprites/low-detail-building-d.png +0 -0
  186. package/city/web/dist/sprites/low-detail-building-e.png +0 -0
  187. package/city/web/dist/sprites/low-detail-building-f.png +0 -0
  188. package/city/web/dist/sprites/low-detail-building-g.png +0 -0
  189. package/city/web/dist/sprites/low-detail-building-h.png +0 -0
  190. package/city/web/dist/sprites/low-detail-building-i.png +0 -0
  191. package/city/web/dist/sprites/low-detail-building-j.png +0 -0
  192. package/city/web/dist/sprites/low-detail-building-k.png +0 -0
  193. package/city/web/dist/sprites/low-detail-building-l.png +0 -0
  194. package/city/web/dist/sprites/low-detail-building-m.png +0 -0
  195. package/city/web/dist/sprites/low-detail-building-n.png +0 -0
  196. package/city/web/dist/sprites/low-detail-building-wide-a.png +0 -0
  197. package/city/web/dist/sprites/low-detail-building-wide-b.png +0 -0
  198. package/city/web/dist/sprites/medidas.json +248 -0
  199. package/city/web/dist-hall/hall.js +264 -0
  200. package/city/web/index.html +294 -0
  201. package/city/web/package.json +18 -0
  202. package/city/web/sella.py +21 -0
  203. package/city/web/src/activity-actors.ts +47 -0
  204. package/city/web/src/activity.ts +172 -0
  205. package/city/web/src/ayuntamiento.ts +580 -0
  206. package/city/web/src/draw.ts +640 -0
  207. package/city/web/src/game-speech.ts +79 -0
  208. package/city/web/src/hall.ts +1867 -0
  209. package/city/web/src/main.ts +2225 -0
  210. package/city/web/src/oven.ts +124 -0
  211. package/city/web/src/people.ts +244 -0
  212. package/city/web/src/presencia.ts +193 -0
  213. package/city/web/src/puertas.ts +180 -0
  214. package/city/web/src/repo-roles.ts +59 -0
  215. package/city/worker/package.json +16 -0
  216. package/city/worker/schema.sql +117 -0
  217. package/city/worker/src/index.ts +702 -0
  218. package/city/worker/src/square.ts +218 -0
  219. package/city/worker/wrangler.toml +58 -0
  220. package/demo/ada.md +50 -0
  221. package/demo/bruno.md +62 -0
  222. package/demo/camila.md +56 -0
  223. package/demo/city/ada.md +40 -0
  224. package/demo/city/city.yml +8 -0
  225. package/demo/city/parcels.yml +15 -0
  226. package/demo/city/roads.json +4 -0
  227. package/demo/city/units.yml +10 -0
  228. package/demo/clinica/city.yml +8 -0
  229. package/demo/clinica/parcels.yml +15 -0
  230. package/demo/clinica/roads.json +4 -0
  231. package/demo/clinica/units.yml +9 -0
  232. package/demo/clinica/vera.md +46 -0
  233. package/demo/dante.md +54 -0
  234. package/demo/despacho/city.yml +8 -0
  235. package/demo/despacho/marta.md +46 -0
  236. package/demo/despacho/parcels.yml +15 -0
  237. package/demo/despacho/roads.json +4 -0
  238. package/demo/despacho/units.yml +9 -0
  239. package/demo/elsa.md +50 -0
  240. package/demo/farid.md +52 -0
  241. package/demo/greta.md +50 -0
  242. package/demo/hugo.md +49 -0
  243. package/demo/iris.md +49 -0
  244. package/demo/jonas.md +51 -0
  245. package/demo/kira.md +51 -0
  246. package/demo/luca.md +49 -0
  247. package/demo/parcels.yml +92 -0
  248. package/demo/seed.py +20 -0
  249. package/demo/show.py +161 -0
  250. package/demo/stories.py +824 -0
  251. package/demo/units.yml +33 -0
  252. package/docs/agents-first.md +99 -0
  253. package/docs/glossary.md +55 -0
  254. package/docs/map-live-layers.md +73 -0
  255. package/docs/security.md +231 -0
  256. package/docs/self-host.md +151 -0
  257. package/docs/testing.md +205 -0
  258. package/package.json +73 -0
  259. package/plugin/.claude-plugin/plugin.json +62 -0
  260. package/plugin/.mcp.json +15 -0
  261. package/plugin/channel/activity-cli.ts +16 -0
  262. package/plugin/channel/adapter-prompts.ts +113 -0
  263. package/plugin/channel/adapter.js +4292 -0
  264. package/plugin/channel/adapter.ts +133 -0
  265. package/plugin/channel/bus.js +20188 -0
  266. package/plugin/channel/bus.ts +131 -0
  267. package/plugin/channel/city-config.ts +125 -0
  268. package/plugin/channel/claude-channel.ts +58 -0
  269. package/plugin/channel/cli-args.ts +75 -0
  270. package/plugin/channel/client.js +4200 -0
  271. package/plugin/channel/client.ts +38 -0
  272. package/plugin/channel/committee/activity.ts +256 -0
  273. package/plugin/channel/committee/collection.ts +154 -0
  274. package/plugin/channel/committee/decision.ts +175 -0
  275. package/plugin/channel/committee/floor.ts +135 -0
  276. package/plugin/channel/committee/guards.ts +30 -0
  277. package/plugin/channel/committee/history.ts +38 -0
  278. package/plugin/channel/committee/render.ts +102 -0
  279. package/plugin/channel/committee/service.ts +72 -0
  280. package/plugin/channel/committee/storage.ts +92 -0
  281. package/plugin/channel/committee/types.ts +163 -0
  282. package/plugin/channel/committee/view.ts +63 -0
  283. package/plugin/channel/committee-cli.ts +109 -0
  284. package/plugin/channel/delivery-metrics.ts +61 -0
  285. package/plugin/channel/delivery-queue.ts +118 -0
  286. package/plugin/channel/hub/activity-controller.ts +72 -0
  287. package/plugin/channel/hub/activity-feed.ts +169 -0
  288. package/plugin/channel/hub/committee-controller.ts +60 -0
  289. package/plugin/channel/hub/connections.ts +48 -0
  290. package/plugin/channel/hub/diagnostics.ts +67 -0
  291. package/plugin/channel/hub/envelope-validity.ts +109 -0
  292. package/plugin/channel/hub/envelopes.ts +66 -0
  293. package/plugin/channel/hub/lifecycle.ts +91 -0
  294. package/plugin/channel/hub/local-roads.ts +89 -0
  295. package/plugin/channel/hub/remote-roads.ts +165 -0
  296. package/plugin/channel/hub/road-controller.ts +99 -0
  297. package/plugin/channel/hub-client.ts +149 -0
  298. package/plugin/channel/local-hub.js +5995 -0
  299. package/plugin/channel/local-hub.ts +349 -0
  300. package/plugin/channel/map-reporter.ts +48 -0
  301. package/plugin/channel/package-lock.json +1731 -0
  302. package/plugin/channel/package.json +19 -0
  303. package/plugin/channel/protocol.ts +98 -0
  304. package/plugin/channel/road-cli.ts +18 -0
  305. package/plugin/channel/run.sh +15 -0
  306. package/plugin/channel/runtime/claude.ts +458 -0
  307. package/plugin/channel/runtime/codex-config.ts +77 -0
  308. package/plugin/channel/runtime/codex.ts +669 -0
  309. package/plugin/channel/runtime/command.ts +62 -0
  310. package/plugin/channel/runtime/factory.ts +15 -0
  311. package/plugin/channel/runtime/json-rpc.ts +154 -0
  312. package/plugin/channel/runtime/kimi.ts +277 -0
  313. package/plugin/channel/runtime/opencode.ts +248 -0
  314. package/plugin/channel/runtime/process.ts +84 -0
  315. package/plugin/channel/runtime/types.ts +45 -0
  316. package/plugin/channel/runtime-files.ts +73 -0
  317. package/plugin/channel/runtime-gateway.js +6396 -0
  318. package/plugin/channel/runtime-gateway.ts +358 -0
  319. package/plugin/channel/runtime-metrics.ts +52 -0
  320. package/plugin/channel/runtime-subscription.ts +180 -0
  321. package/plugin/channel/terminal-delivery.ts +131 -0
  322. package/plugin/channel/untrusted.ts +60 -0
  323. package/plugin/commands/committee.md +31 -0
  324. package/plugin/commands/exit.md +32 -0
  325. package/plugin/commands/goals.md +20 -0
  326. package/plugin/commands/join.md +18 -0
  327. package/plugin/commands/notice.md +20 -0
  328. package/plugin/commands/propose.md +19 -0
  329. package/plugin/commands/round.md +17 -0
  330. package/plugin/commands/session.md +22 -0
  331. package/plugin/commands/settings.md +25 -0
  332. package/plugin/commands/setup.md +21 -0
  333. package/plugin/commands/team.md +23 -0
  334. package/plugin/domains/custom.md +27 -0
  335. package/plugin/domains/finance.md +31 -0
  336. package/plugin/domains/healthcare.md +34 -0
  337. package/plugin/domains/legal.md +31 -0
  338. package/plugin/domains/marketing.md +33 -0
  339. package/plugin/domains/operations.md +31 -0
  340. package/plugin/domains/research.md +32 -0
  341. package/plugin/domains/sales.md +30 -0
  342. package/plugin/domains/software.md +37 -0
  343. package/plugin/hooks/activity.sh +6 -0
  344. package/plugin/hooks/digging.sh +68 -0
  345. package/plugin/hooks/growth.sh +42 -0
  346. package/plugin/hooks/hooks.json +106 -0
  347. package/plugin/hooks/notice-on-pr.sh +35 -0
  348. package/plugin/hooks/notice-on-stop.sh +80 -0
  349. package/plugin/hooks/notice-pending.sh +58 -0
  350. package/plugin/hooks/solo-en-ciudad.sh +23 -0
  351. package/plugin/hooks/tokens.sh +47 -0
  352. package/plugin/roles/examples/account-executive.md +25 -0
  353. package/plugin/roles/examples/ai-manager.md +34 -0
  354. package/plugin/roles/examples/associate.md +24 -0
  355. package/plugin/roles/examples/brand-lead.md +20 -0
  356. package/plugin/roles/examples/cfo.md +20 -0
  357. package/plugin/roles/examples/city-lead.md +25 -0
  358. package/plugin/roles/examples/clinical-director.md +26 -0
  359. package/plugin/roles/examples/clinical-ops.md +25 -0
  360. package/plugin/roles/examples/clinician.md +25 -0
  361. package/plugin/roles/examples/compliance.md +27 -0
  362. package/plugin/roles/examples/content.md +24 -0
  363. package/plugin/roles/examples/controller.md +27 -0
  364. package/plugin/roles/examples/cpto.md +41 -0
  365. package/plugin/roles/examples/customer-success.md +25 -0
  366. package/plugin/roles/examples/data-engineer.md +37 -0
  367. package/plugin/roles/examples/data.md +60 -0
  368. package/plugin/roles/examples/dev.md +37 -0
  369. package/plugin/roles/examples/devops.md +39 -0
  370. package/plugin/roles/examples/enablement.md +24 -0
  371. package/plugin/roles/examples/ethics.md +24 -0
  372. package/plugin/roles/examples/fin-analytics.md +27 -0
  373. package/plugin/roles/examples/health-compliance.md +25 -0
  374. package/plugin/roles/examples/health-data.md +26 -0
  375. package/plugin/roles/examples/knowledge.md +25 -0
  376. package/plugin/roles/examples/lifecycle.md +26 -0
  377. package/plugin/roles/examples/llm-engineer.md +34 -0
  378. package/plugin/roles/examples/managing-partner.md +20 -0
  379. package/plugin/roles/examples/methods.md +23 -0
  380. package/plugin/roles/examples/operations-lead.md +23 -0
  381. package/plugin/roles/examples/ops.md +24 -0
  382. package/plugin/roles/examples/patient-safety.md +26 -0
  383. package/plugin/roles/examples/performance.md +27 -0
  384. package/plugin/roles/examples/po.md +36 -0
  385. package/plugin/roles/examples/process-owner.md +23 -0
  386. package/plugin/roles/examples/product-design.md +39 -0
  387. package/plugin/roles/examples/program-manager.md +24 -0
  388. package/plugin/roles/examples/quality.md +25 -0
  389. package/plugin/roles/examples/research-director.md +24 -0
  390. package/plugin/roles/examples/research-ops.md +25 -0
  391. package/plugin/roles/examples/researcher.md +25 -0
  392. package/plugin/roles/examples/revenue-lead.md +25 -0
  393. package/plugin/roles/examples/revops.md +26 -0
  394. package/plugin/roles/examples/seo.md +27 -0
  395. package/plugin/roles/examples/specialist.md +24 -0
  396. package/plugin/scripts/admision.py +165 -0
  397. package/plugin/scripts/apaga.py +232 -0
  398. package/plugin/scripts/avatar.py +211 -0
  399. package/plugin/scripts/broker.py +531 -0
  400. package/plugin/scripts/cage.py +252 -0
  401. package/plugin/scripts/capabilities.py +197 -0
  402. package/plugin/scripts/card.py +312 -0
  403. package/plugin/scripts/cities.py +520 -0
  404. package/plugin/scripts/city-env.sh +90 -0
  405. package/plugin/scripts/city-runtime.sh +87 -0
  406. package/plugin/scripts/city-session.sh +526 -0
  407. package/plugin/scripts/city_env.py +67 -0
  408. package/plugin/scripts/crecimiento.py +140 -0
  409. package/plugin/scripts/deliberations.py +48 -0
  410. package/plugin/scripts/doctor.py +172 -0
  411. package/plugin/scripts/domains.py +201 -0
  412. package/plugin/scripts/evidencia.py +64 -0
  413. package/plugin/scripts/find-repos.sh +111 -0
  414. package/plugin/scripts/gh.py +159 -0
  415. package/plugin/scripts/hall_protocol.py +147 -0
  416. package/plugin/scripts/hook_activity.py +139 -0
  417. package/plugin/scripts/launch.py +94 -0
  418. package/plugin/scripts/logs.py +98 -0
  419. package/plugin/scripts/pairing.py +185 -0
  420. package/plugin/scripts/parcels.py +132 -0
  421. package/plugin/scripts/read-card.py +51 -0
  422. package/plugin/scripts/report.py +201 -0
  423. package/plugin/scripts/reset.py +180 -0
  424. package/plugin/scripts/roads.py +230 -0
  425. package/plugin/scripts/roles.py +274 -0
  426. package/plugin/scripts/runtime_log.py +69 -0
  427. package/plugin/scripts/runtime_processes.py +132 -0
  428. package/plugin/scripts/rutas.py +83 -0
  429. package/plugin/scripts/seat.py +1349 -0
  430. package/plugin/scripts/tokens.py +194 -0
  431. package/plugin/scripts/trust-repos.py +59 -0
  432. package/plugin/scripts/ui.py +280 -0
  433. package/plugin/scripts/units.py +80 -0
  434. package/plugin/scripts/workspace.py +364 -0
  435. package/plugin/skills/city/SKILL.md +191 -0
  436. package/templates/blank.md +39 -0
  437. package/templates/finance.md +44 -0
  438. package/templates/legal.md +45 -0
  439. package/templates/marketing.md +48 -0
  440. package/templates/product.md +45 -0
package/bin/skills ADDED
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env bash
2
+ # Recognise repo-local skills without installing or changing them.
3
+ exec python3 "$(cd "$(dirname "$0")/../plugin/scripts" && pwd)/capabilities.py" "$@"
package/bin/test ADDED
@@ -0,0 +1,131 @@
1
+ #!/usr/bin/env bash
2
+ # Every test in the repo, in one command.
3
+ #
4
+ # ./bin/test all of it
5
+ # ./bin/test card seat just these
6
+ #
7
+ # Twelve suites and two compatibility walks. Nothing here needs a network, an
8
+ # account, a terminal or a deployed anything: the widgets run against a fake curses
9
+ # window, the reporter against a temporary git repo, and the wizard against a file
10
+ # of answers. A suite that needs a browser open is a suite nobody runs.
11
+ set -uo pipefail
12
+ cd "$(dirname "$0")/.." || exit 1
13
+
14
+ # Arm the commit-time gate while we are here. Git refuses to ship hooks inside a
15
+ # repo (on purpose), so the first test run in a clone points this clone at ours.
16
+ # Installed copies are not git repos and skip this silently.
17
+ if [ -d .git ] && [ -x .githooks/pre-commit ] \
18
+ && [ -z "$(git config core.hooksPath 2>/dev/null)" ]; then
19
+ git config core.hooksPath .githooks \
20
+ && printf ' pre-commit gate armed (core.hooksPath .githooks)\n'
21
+ fi
22
+
23
+ SUITES=(widgets card parcels domains serve seat cities channel 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 launch)
24
+ [ $# -gt 0 ] && SUITES=("$@")
25
+
26
+ fallos=0
27
+ total=0
28
+
29
+ printf '\n'
30
+ for s in "${SUITES[@]}"; do
31
+ guion="bin/test-$s.py"
32
+ if [ ! -x "$guion" ]; then
33
+ printf ' %-10s \033[31mno such suite\033[0m (%s)\n' "$s" "$guion"
34
+ fallos=$((fallos + 1)); continue
35
+ fi
36
+ salida="$(./"$guion" 2>&1)"
37
+ if [ $? -eq 0 ]; then
38
+ n="$(printf '%s' "$salida" | grep -oE '[0-9]+ checks' | tail -1)"
39
+ printf ' %-10s \033[32mok\033[0m %s\n' "$s" "${n:-}"
40
+ total=$((total + $(printf '%s' "${n:-0}" | grep -oE '^[0-9]+' || echo 0)))
41
+ else
42
+ printf ' %-10s \033[31mFAILED\033[0m\n' "$s"
43
+ printf '%s\n' "$salida" | sed 's/^/ /'
44
+ fallos=$((fallos + 1))
45
+ fi
46
+ done
47
+
48
+ if [ $# -eq 0 ]; then
49
+ if ./bin/setup.py --help 2>&1 | grep -q 'one personal Agents City' \
50
+ && ! ./bin/setup.py --help 2>&1 | grep -q 'legacy-team'; then
51
+ printf ' %-10s \033[32mok\033[0m public setup exposes only personal cities\n' "setup"
52
+ else
53
+ printf ' %-10s \033[31mFAILED\033[0m\n' "setup"
54
+ fallos=$((fallos + 1))
55
+ fi
56
+
57
+ # The map fixture remains a compatibility input for the visual seeder.
58
+ if [ "$(./city/scripts/seed.py --data demo --fake-history 2>/dev/null \
59
+ | grep -c 'INSERT INTO parcela')" = 34 ]; then
60
+ printf ' %-10s \033[32mok\033[0m the demo still seeds 34 parcels\n' "seeder"
61
+ else
62
+ printf ' %-10s \033[31mFAILED\033[0m\n' "seeder"
63
+ fallos=$((fallos + 1))
64
+ fi
65
+ fi
66
+
67
+ # The npm package: every path the shim dispatches to must actually ship. This is
68
+ # the check that catches "added a module, forgot the files whitelist" — the shim
69
+ # would break only on the installed machine, where nobody is looking.
70
+ if [ $# -eq 0 ] && command -v npm >/dev/null 2>&1; then
71
+ faltan=$(npm pack --dry-run --json 2>/dev/null | python3 -c '
72
+ import json, sys
73
+ salida = json.load(sys.stdin)
74
+ dentro = {f["path"] for f in salida[0]["files"]}
75
+ necesarios = ["bin/agents-city.js", "bin/hall", "bin/seat", "bin/city", "bin/demo",
76
+ "bin/setup.py", "bin/serve.py", "bin/hall.html",
77
+ "bin/ui.py" if "bin/ui.py" in dentro else "plugin/scripts/ui.py",
78
+ "plugin/scripts/seat.py", "plugin/scripts/card.py",
79
+ "plugin/scripts/parcels.py", "plugin/scripts/units.py",
80
+ "plugin/scripts/roles.py", "plugin/scripts/gh.py",
81
+ "plugin/scripts/domains.py", "plugin/domains/software.md",
82
+ "plugin/roles/examples/data-engineer.md",
83
+ "plugin/scripts/cities.py", "plugin/scripts/roads.py",
84
+ "plugin/scripts/cage.py", "plugin/scripts/broker.py",
85
+ "plugin/scripts/rutas.py", "plugin/scripts/evidencia.py",
86
+ "plugin/scripts/admision.py", "plugin/scripts/pairing.py",
87
+ "plugin/scripts/hall_protocol.py", "plugin/scripts/doctor.py",
88
+ "plugin/scripts/workspace.py", "plugin/scripts/avatar.py",
89
+ "plugin/scripts/crecimiento.py",
90
+ "plugin/scripts/reset.py", "plugin/scripts/capabilities.py",
91
+ "plugin/scripts/deliberations.py",
92
+ "plugin/scripts/runtime_processes.py",
93
+ "plugin/scripts/runtime_log.py", "plugin/scripts/logs.py",
94
+ "plugin/scripts/launch.py", "plugin/scripts/hook_activity.py",
95
+ "plugin/scripts/city-session.sh", "plugin/scripts/city-runtime.sh",
96
+ "plugin/scripts/find-repos.sh",
97
+ "plugin/channel/bus.js", "plugin/channel/local-hub.js",
98
+ "plugin/channel/client.js", "plugin/channel/adapter.js",
99
+ "plugin/channel/runtime-gateway.js",
100
+ "plugin/.mcp.json",
101
+ "bin/cities", "bin/road", "bin/bus", "bin/committee", "bin/agents",
102
+ "bin/reset", "bin/skills", "bin/benchmark", "bin/logs",
103
+ "benchmarks/committee/run.py",
104
+ "benchmarks/latency/live.py", "benchmarks/latency/README.md",
105
+ "benchmarks/latency/fake-native-server.mjs",
106
+ "benchmarks/latency/fake-claude-cli.mjs",
107
+ "benchmarks/stress/run.py", "benchmarks/stress/README.md",
108
+ "city/web/src/repo-roles.ts",
109
+ "demo/show.py", "demo/city/city.yml", "demo/city/ada.md",
110
+ "city/scripts/seed.py", "city/worker/schema.sql", "demo/parcels.yml"]
111
+ problemas = [f"missing: {n}" for n in necesarios if n not in dentro]
112
+ basura = ("/node_modules/", "/__pycache__/", "/.wrangler/")
113
+ for ruta in sorted(dentro):
114
+ if any(x in f"/{ruta}/" for x in basura) or ruta.endswith((".pyc", "/.DS_Store")):
115
+ problemas.append(f"development artifact shipped: {ruta}")
116
+ print("\n".join(problemas))')
117
+ if [ -z "$faltan" ]; then
118
+ printf ' %-10s \033[32mok\033[0m every path the shim needs ships in the tarball\n' "package"
119
+ else
120
+ printf ' %-10s \033[31mFAILED\033[0m — invalid npm package contents:\n' "package"
121
+ printf '%s\n' "$faltan" | sed 's/^/ /'
122
+ fallos=$((fallos + 1))
123
+ fi
124
+ fi
125
+
126
+ printf '\n'
127
+ if [ "$fallos" -gt 0 ]; then
128
+ printf ' \033[31m%d suite(s) failed\033[0m\n\n' "$fallos"
129
+ exit 1
130
+ fi
131
+ printf ' \033[32mall green\033[0m — %d checks\n\n' "$total"
@@ -0,0 +1,183 @@
1
+ #!/usr/bin/env python3
2
+ """Explicit unknown-CLI fallback remains safe, slow and visibly separate."""
3
+ import json
4
+ import datetime
5
+ import os
6
+ import shutil
7
+ import subprocess
8
+ import sys
9
+ import tempfile
10
+ import time
11
+
12
+ AQUI = os.path.dirname(os.path.abspath(__file__))
13
+ RAIZ = os.path.dirname(AQUI)
14
+ sys.path.insert(0, AQUI)
15
+ from testlib import ( # noqa: E402
16
+ afirma, comprueba, detiene_hubs_de_ciudad, hubs_de_ciudad, resumen,
17
+ )
18
+
19
+ ADAPTER = os.path.join(RAIZ, 'plugin', 'channel', 'adapter.js')
20
+ CLIENT = os.path.join(RAIZ, 'plugin', 'channel', 'client.js')
21
+ FAKE_TUI = os.path.join(RAIZ, 'benchmarks', 'latency', 'fake-tui.py')
22
+ RUNTIMES = {
23
+ 'legacy-one': 'custom-tui',
24
+ 'legacy-two': 'custom-tui',
25
+ 'legacy-three': 'custom-tui',
26
+ 'legacy-four': 'custom-tui',
27
+ }
28
+
29
+
30
+ def espera(condicion, segundos=8):
31
+ limite = time.monotonic() + segundos
32
+ while time.monotonic() < limite:
33
+ if condicion():
34
+ return True
35
+ time.sleep(.1)
36
+ return False
37
+
38
+
39
+ def main():
40
+ print('\n explicit terminal fallback for unknown CLIs')
41
+ if not shutil.which('tmux'):
42
+ afirma('· tmux is installed', False, 'tmux missing')
43
+ return resumen('adapter')
44
+ base = tempfile.mkdtemp(prefix='agents-city-adapter-')
45
+ app = os.path.join(base, 'app')
46
+ city = os.path.join(app, 'alice', 'home')
47
+ os.makedirs(city)
48
+ repos = list(RUNTIMES)
49
+ open(os.path.join(city, 'city.yml'), 'w', encoding='utf-8').write(
50
+ 'id: city_adapter\nname: Home\nslug: home\nowner: alice\n')
51
+ open(os.path.join(city, 'roads.json'), 'w', encoding='utf-8').write(
52
+ '{"version": 1, "roads": []}\n')
53
+ runs = ''.join(f'runs.{actor}: {runtime}\n'
54
+ for actor, runtime in RUNTIMES.items())
55
+ roles = ''.join(f'role.{actor}: {"seo" if actor == "legacy-two" else "blank"}\n'
56
+ for actor in RUNTIMES)
57
+ open(os.path.join(city, 'alice.md'), 'w', encoding='utf-8').write(
58
+ f'---\nuser: alice\nagent: alice/ceo\nrepos: [{", ".join(repos)}]\n'
59
+ f'{roles}{runs}---\n')
60
+ env = dict(os.environ, AGENTS_CITY_HOME=app, AGENTS_CITY_DATA=city,
61
+ AGENTS_CITY_USER='alice')
62
+ for key in ('CITY_BUS_URL', 'CITY_BUS_TOKEN', 'CITY_DIR'):
63
+ env.pop(key, None)
64
+ session = f'ac-adapter-{os.getpid()}'
65
+ adapters = []
66
+ endpoint = os.path.join(app, '.runtime', 'bus', 'city-adapter', 'endpoint.json')
67
+ hub_pid = 0
68
+ try:
69
+ for actor in repos:
70
+ capture = os.path.join(base, f'{actor}.txt')
71
+ subprocess.run(
72
+ ['tmux', 'new-session' if actor == repos[0] else 'new-window',
73
+ '-d' if actor == repos[0] else '-d',
74
+ '-s' if actor == repos[0] else '-t',
75
+ session if actor == repos[0] else session,
76
+ '-n', actor, sys.executable, FAKE_TUI, capture,
77
+ '--settle-ms', '120'],
78
+ check=True, capture_output=True, text=True)
79
+ for actor, runtime in RUNTIMES.items():
80
+ process = subprocess.Popen(
81
+ ['node', ADAPTER, '--data', city, '--actor', actor,
82
+ '--target', f'{session}:{actor}', '--runtime', runtime],
83
+ env=dict(env, CITY_BUS_ACTOR=actor, CITY_AGENT_RUNTIME=runtime,
84
+ CITY_TERMINAL_READY_DELAY_MS='40',
85
+ CITY_TERMINAL_SUBMIT_DELAY_MS='180'),
86
+ stdout=subprocess.DEVNULL, stderr=subprocess.PIPE, text=True)
87
+ adapters.append(process)
88
+ afirma('· all four explicit fallback adapters authenticate',
89
+ espera(lambda: all(p.poll() is None for p in adapters), .5))
90
+
91
+ payload = {
92
+ 'question': 'Which runtime received this?',
93
+ 'desiredOutcome': 'The same authenticated brief everywhere',
94
+ 'definitionOfDone': ['every capture has the protocol prompt'],
95
+ 'participants': repos,
96
+ }
97
+ opened = subprocess.run(
98
+ ['node', CLIENT, 'committee', 'open', '--input', '-'],
99
+ input=json.dumps(payload), capture_output=True, text=True,
100
+ env=dict(env, CITY_BUS_ACTOR='seat'), timeout=12)
101
+ state = json.loads(opened.stdout) if opened.returncode == 0 else {}
102
+ thread = state.get('id', '')
103
+ afirma('· the chair emits one typed assignment per selected runtime',
104
+ bool(thread), opened.stderr.strip())
105
+
106
+ for actor, runtime in RUNTIMES.items():
107
+ capture = os.path.join(base, f'{actor}.txt')
108
+ delivered = espera(
109
+ lambda p=capture: os.path.exists(p) and bool(
110
+ open(p, encoding='utf-8', errors='ignore').read().strip()))
111
+ records = [json.loads(line) for line in open(
112
+ capture, encoding='utf-8', errors='ignore') if line.strip()
113
+ ] if os.path.exists(capture) else []
114
+ body = records[-1]['body'] if records else ''
115
+ afirma(f'· {runtime} receives the same chaired assignment',
116
+ delivered and thread in body
117
+ and 'agents-city committee respond' in body
118
+ and 'Do not contact another repo agent directly' in body,
119
+ body[-160:])
120
+ esperado = (
121
+ 'operating role is seo'
122
+ if actor == 'legacy-two'
123
+ else 'operating role is blank'
124
+ )
125
+ afirma(
126
+ f'· {runtime} receives its own operating role',
127
+ esperado in body,
128
+ body[-300:],
129
+ )
130
+
131
+ outbox = os.path.join(app, '.runtime', 'bus', 'city-adapter', 'outbox')
132
+ drained = espera(
133
+ lambda: all(not os.path.isdir(os.path.join(outbox, actor))
134
+ or not os.listdir(os.path.join(outbox, actor))
135
+ for actor in repos))
136
+ afirma('· delivery acknowledgements drain every durable actor outbox', drained)
137
+ metrics_path = os.path.join(
138
+ app, '.runtime', 'bus', 'city-adapter', 'delivery-latency.jsonl')
139
+ metrics = [json.loads(line) for line in open(metrics_path, encoding='utf-8')
140
+ if line.strip()] if os.path.exists(metrics_path) else []
141
+ actors = {row.get('actor') for row in metrics}
142
+ afirma('· every accepted prompt has an auditable latency trace',
143
+ actors == set(repos), str(actors))
144
+ afirma('· Enter is delayed until each multiline paste has settled',
145
+ metrics and all(row.get('pasteToSubmitMs', 0) >= 150
146
+ for row in metrics), str(metrics))
147
+ submitted = [row['submittedAt'] for row in metrics]
148
+ instants = [datetime.datetime.fromisoformat(
149
+ value.replace('Z', '+00:00')).timestamp() for value in submitted]
150
+ spread = max(instants) - min(instants) if instants else 99
151
+ afirma('· four terminal fallbacks submit in parallel, not one after another',
152
+ spread < 1.0, f'{spread:.3f}s')
153
+ data = json.load(open(endpoint, encoding='utf-8'))
154
+ hub_pid = int(data.get('pid', 0))
155
+ comprueba('· all runtimes used one city WebSocket endpoint',
156
+ data.get('protocol'), 'agents-city-bus/2')
157
+ comprueba('· concurrent adapter startup creates exactly one city hub',
158
+ len(hubs_de_ciudad(city)), 1)
159
+ finally:
160
+ for process in adapters:
161
+ if process.poll() is None:
162
+ process.terminate()
163
+ for process in adapters:
164
+ try:
165
+ process.wait(timeout=2)
166
+ except subprocess.TimeoutExpired:
167
+ process.kill()
168
+ subprocess.run(['tmux', 'kill-session', '-t', session],
169
+ capture_output=True)
170
+ if os.path.exists(endpoint):
171
+ try:
172
+ data = json.load(open(endpoint, encoding='utf-8'))
173
+ hub_pid = int(data['pid'])
174
+ except (OSError, ValueError, KeyError, json.JSONDecodeError):
175
+ pass
176
+ limpio = detiene_hubs_de_ciudad(city)
177
+ afirma('· fallback cleanup leaves no orphan city hub', limpio, str(hub_pid))
178
+ shutil.rmtree(base, ignore_errors=True)
179
+ return resumen('adapter')
180
+
181
+
182
+ if __name__ == '__main__':
183
+ sys.exit(main())
@@ -0,0 +1,99 @@
1
+ #!/usr/bin/env python3
2
+ """Admission as an ordered gate graph: the decisive gate, the reason, redaction.
3
+
4
+ Pure and transport-free. The cases that matter: the first blocker is the
5
+ decisive gate; a road that matches but whose sender does not is an explicit
6
+ block, not a fallthrough; an unknown sender under pairing yields a pairing
7
+ requirement rather than a silent drop; and diagnostics never carry a raw
8
+ allowlist entry.
9
+ """
10
+
11
+ import os
12
+ import sys
13
+
14
+ AQUI = os.path.dirname(os.path.abspath(__file__))
15
+ RAIZ = os.path.dirname(AQUI)
16
+ sys.path.insert(0, os.path.join(RAIZ, "plugin", "scripts"))
17
+ sys.path.insert(0, AQUI)
18
+
19
+ import admision as ad # noqa: E402
20
+ from testlib import afirma, comprueba, resumen # noqa: E402
21
+
22
+ FELIZ = dict(road_existe=True, road_incidente=False, direccion_coincide=True,
23
+ emparejado=False, allowlist=["alice/home"], payload_valido=True,
24
+ texto_no_vacio=True)
25
+
26
+
27
+ def dispatch():
28
+ d = ad.decide_entrada(**FELIZ)
29
+ afirma("a fully valid inbound dispatches", d.permitido())
30
+ comprueba("its reason is road_allowed", d.razon, "road_allowed")
31
+ afirma("the graph records every phase it walked", len(d.grafo) >= 3)
32
+ afirma("a decision exposes no evidence field to mistake for authorization",
33
+ not hasattr(d, "evidencia") and "evidence" not in d.como_dict())
34
+
35
+
36
+ def bloqueos():
37
+ d = ad.decide_entrada(**{**FELIZ, "road_existe": False})
38
+ comprueba("no road -> skip", d.admision, ad.Admision.OMITE)
39
+ comprueba("decisive gate is the road gate", d.puerta_decisiva, "road")
40
+ comprueba("reason is road_missing", d.razon, "road_missing")
41
+
42
+ d = ad.decide_entrada(**{**FELIZ, "road_incidente": True})
43
+ comprueba("a road under incident drops", d.admision, ad.Admision.DESCARTA)
44
+ afirma("a dropped decision never reports itself as permitted", not d.permitido())
45
+
46
+ d = ad.decide_entrada(**{**FELIZ, "dirigido_aqui": False})
47
+ comprueba("a message not addressed here is skipped", d.admision, ad.Admision.OMITE)
48
+ comprueba("and address is the decisive gate", d.puerta_decisiva, "address")
49
+
50
+ d = ad.decide_entrada(**{**FELIZ, "payload_valido": False})
51
+ comprueba("a malformed payload drops", d.admision, ad.Admision.DESCARTA)
52
+ d = ad.decide_entrada(**{**FELIZ, "texto_no_vacio": False})
53
+ comprueba("empty text drops", d.razon, "payload_empty")
54
+
55
+
56
+ def emparejamiento():
57
+ base = {**FELIZ, "direccion_coincide": False}
58
+ d = ad.decide_entrada(**{**base, "emparejado": False})
59
+ comprueba("an unknown sender under pairing asks to pair, not drops",
60
+ d.admision, ad.Admision.EMPAREJA)
61
+ comprueba("reason is sender_not_paired", d.razon, "sender_not_paired")
62
+ d = ad.decide_entrada(**{**base, "emparejado": True})
63
+ afirma("a paired-but-not-in-config sender dispatches", d.permitido())
64
+ afirma("and the pairing fact is recorded as a gate in the graph",
65
+ any(g.razon == "sender_paired" for g in d.grafo))
66
+
67
+
68
+ def orden_y_redaccion():
69
+ # Both the road and the payload are bad; the earlier (road) gate must win.
70
+ d = ad.decide_entrada(**{**FELIZ, "road_existe": False, "payload_valido": False})
71
+ comprueba("the FIRST blocker is decisive, not the last", d.puerta_decisiva, "road")
72
+
73
+ d = ad.decide_entrada(**{**FELIZ, "allowlist": ["alice/home", "bob/research"]})
74
+ diag = next(g.diagnostico for g in d.grafo if g.diagnostico)
75
+ comprueba("the diagnostic reveals the count", diag["n"], 2)
76
+ afirma("but never a raw allowlist entry",
77
+ "alice/home" not in str(diag) and "bob/research" not in str(diag))
78
+ afirma("opaque ids are stable and prefixed",
79
+ all(i.startswith("x") for i in diag["ids"]))
80
+ comprueba("the same entry hashes to the same opaque id",
81
+ ad.id_opaco("alice/home"), ad.id_opaco("alice/home"))
82
+
83
+
84
+ def contrato_razones():
85
+ try:
86
+ ad.Puerta("x", "route", False, "not_a_real_reason")
87
+ afirma("a reason outside the closed set is rejected", False)
88
+ except ValueError:
89
+ afirma("a reason outside the closed set is rejected", True)
90
+ afirma("como_dict is JSON-shaped for the wire",
91
+ set(ad.decide_entrada(**FELIZ).como_dict()) >= {"admission", "reasonCode", "gates"})
92
+
93
+
94
+ dispatch()
95
+ bloqueos()
96
+ emparejamiento()
97
+ orden_y_redaccion()
98
+ contrato_razones()
99
+ sys.exit(resumen("admision"))
@@ -0,0 +1,58 @@
1
+ #!/usr/bin/env python3
2
+ """Deterministic agent avatars: same name → same face, self-contained SVG.
3
+
4
+ Pure. The load-bearing cases: determinism, distinctness, symmetry, that the
5
+ output is a self-contained SVG with no external reference (Hall CSP), and that
6
+ the kind tints the border.
7
+ """
8
+
9
+ import os
10
+ import sys
11
+
12
+ AQUI = os.path.dirname(os.path.abspath(__file__))
13
+ RAIZ = os.path.dirname(AQUI)
14
+ sys.path.insert(0, os.path.join(RAIZ, "plugin", "scripts"))
15
+ sys.path.insert(0, AQUI)
16
+
17
+ import avatar # noqa: E402
18
+ from testlib import afirma, comprueba, resumen # noqa: E402
19
+
20
+
21
+ def determinismo():
22
+ comprueba("same name and kind produce identical SVG",
23
+ avatar.svg("nova", "code"), avatar.svg("nova", "code"))
24
+ afirma("different names produce different faces",
25
+ avatar.svg("nova") != avatar.svg("store-service"))
26
+ afirma("the empty/None name is handled, not crashed",
27
+ avatar.svg(None).startswith("<svg") and avatar.svg("").startswith("<svg"))
28
+
29
+
30
+ def autocontenido():
31
+ s = avatar.svg("nova")
32
+ afirma("it is an svg element", s.startswith("<svg") and s.endswith("</svg>"))
33
+ afirma("it references no external asset (Hall CSP)",
34
+ "http://www.w3.org/2000/svg" in s
35
+ and "https://" not in s.replace("http://www.w3.org/2000/svg", "")
36
+ and "url(" not in s and "<image" not in s and "<script" not in s)
37
+ afirma("a hostile name cannot break out of the aria-label",
38
+ "<script>" not in avatar.svg('<script>x</script>'))
39
+ afirma("data_uri is a base64 svg data URI",
40
+ avatar.data_uri("nova").startswith("data:image/svg+xml;base64,"))
41
+
42
+
43
+ def clase_y_simetria():
44
+ afirma("kind tints the border colour",
45
+ avatar.TINTE_CLASE["knowledge"] in avatar.svg("x", "knowledge"))
46
+ afirma("an unknown kind falls back to the default tint",
47
+ avatar._TINTE_DEFECTO in avatar.svg("x", "nonsense"))
48
+ # Symmetry: the on-cells are mirrored left↔right, so the set is closed under
49
+ # (row, col) -> (row, 4-col).
50
+ encendidas = avatar._celdas_encendidas(avatar._digest("nova"))
51
+ afirma("the cell pattern is left-right symmetric",
52
+ all((f, avatar._CELDAS - 1 - c) in encendidas for (f, c) in encendidas))
53
+
54
+
55
+ determinismo()
56
+ autocontenido()
57
+ clase_y_simetria()
58
+ sys.exit(resumen("avatar"))
@@ -0,0 +1,40 @@
1
+ #!/usr/bin/env python3
2
+ """The governance benchmark stays honest and machine-readable."""
3
+ import json
4
+ import os
5
+ import subprocess
6
+ import sys
7
+
8
+ AQUI = os.path.dirname(os.path.abspath(__file__))
9
+ RAIZ = os.path.dirname(AQUI)
10
+ sys.path.insert(0, AQUI)
11
+ from testlib import afirma, comprueba, resumen # noqa: E402
12
+
13
+
14
+ def main():
15
+ print('\n committee governance benchmark')
16
+ result = subprocess.run(
17
+ [sys.executable, os.path.join(RAIZ, 'benchmarks', 'committee', 'run.py'),
18
+ '--json'], capture_output=True, text=True)
19
+ afirma('· the benchmark runs without a model account',
20
+ result.returncode == 0, result.stderr.strip())
21
+ report = json.loads(result.stdout)
22
+ comprueba('· its scope refuses an unsupported quality claim',
23
+ report.get('scope'), 'protocol structure, not task-answer quality')
24
+ rows = {row['strategy']: row for row in report['results']}
25
+ afirma('· free mesh exposes lateral edges and pre-barrier influence',
26
+ rows['free-mesh']['lateral_member_edges'] > 0
27
+ and rows['free-mesh']['pre_barrier_leaks'] > 0)
28
+ afirma('· a single agent cannot independently verify itself',
29
+ rows['single-agent']['closures_without_pass'] == 1
30
+ and rows['single-agent']['self_verified_closures'] == 1
31
+ and rows['single-agent']['unattributed_decisions'] == 1)
32
+ afirma('· the chaired trace satisfies every measured invariant',
33
+ rows['chair-mediated']['protocol_violations'] == 0)
34
+ afirma('· that result includes evidence from distinct repo roles',
35
+ rows['chair-mediated']['evidence_sources'] >= 3)
36
+ return resumen('benchmark')
37
+
38
+
39
+ if __name__ == '__main__':
40
+ sys.exit(main())