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
@@ -0,0 +1,379 @@
1
+ #!/usr/bin/env python3
2
+ """Claude Channel delivers the typed WebSocket bus without terminal injection."""
3
+ import json
4
+ import os
5
+ import queue
6
+ import shutil
7
+ import subprocess
8
+ import sys
9
+ import tempfile
10
+ import threading
11
+ import time
12
+
13
+ AQUI = os.path.dirname(os.path.abspath(__file__))
14
+ RAIZ = os.path.dirname(AQUI)
15
+ sys.path.insert(0, AQUI)
16
+ from testlib import afirma, comprueba, detiene_proceso, resumen # noqa: E402
17
+
18
+ CHANNEL = os.path.join(RAIZ, 'plugin', 'channel', 'run.sh')
19
+ CLIENT = os.path.join(RAIZ, 'plugin', 'channel', 'client.js')
20
+
21
+
22
+ def espera(condicion, segundos=8):
23
+ limite = time.monotonic() + segundos
24
+ while time.monotonic() < limite:
25
+ if condicion():
26
+ return True
27
+ time.sleep(.05)
28
+ return False
29
+
30
+
31
+ class Cliente:
32
+ def __init__(self, actor, datos, app, channel=True):
33
+ self.mensajes, self.err = [], []
34
+ self.cola = queue.Queue()
35
+ env = dict(os.environ, CITY_BUS_ACTOR=actor,
36
+ CITY_CLAUDE_CHANNEL='1' if channel else '0',
37
+ AGENTS_CITY_DATA=datos, AGENTS_CITY_HOME=app,
38
+ AGENTS_CITY_USER='alice')
39
+ for key in ('CITY_BUS_URL', 'CITY_BUS_TOKEN', 'CITY_DIR'):
40
+ env.pop(key, None)
41
+ self.p = subprocess.Popen(
42
+ [CHANNEL], stdin=subprocess.PIPE, stdout=subprocess.PIPE,
43
+ stderr=subprocess.PIPE, text=True, bufsize=1, env=env)
44
+ threading.Thread(target=self._lee, daemon=True).start()
45
+ threading.Thread(target=self._lee_err, daemon=True).start()
46
+ self.envia({
47
+ 'jsonrpc': '2.0', 'id': 1, 'method': 'initialize',
48
+ 'params': {'protocolVersion': '2024-11-05', 'capabilities': {},
49
+ 'clientInfo': {'name': 'test', 'version': '1'}},
50
+ })
51
+ self.inicial = self.espera(lambda m: m.get('id') == 1)
52
+ self.envia({'jsonrpc': '2.0', 'method': 'notifications/initialized'})
53
+
54
+ def _lee(self):
55
+ for linea in self.p.stdout:
56
+ try:
57
+ mensaje = json.loads(linea)
58
+ except json.JSONDecodeError:
59
+ continue
60
+ self.mensajes.append(mensaje)
61
+ self.cola.put(mensaje)
62
+
63
+ def _lee_err(self):
64
+ for linea in self.p.stderr:
65
+ self.err.append(linea.rstrip())
66
+
67
+ def envia(self, mensaje):
68
+ self.p.stdin.write(json.dumps(mensaje) + '\n')
69
+ self.p.stdin.flush()
70
+
71
+ def espera(self, condicion, segundos=8):
72
+ limite = time.monotonic() + segundos
73
+ for mensaje in self.mensajes:
74
+ if condicion(mensaje):
75
+ return mensaje
76
+ while time.monotonic() < limite:
77
+ try:
78
+ mensaje = self.cola.get(timeout=max(.01, min(.2, limite - time.monotonic())))
79
+ except queue.Empty:
80
+ continue
81
+ if condicion(mensaje):
82
+ return mensaje
83
+ return {}
84
+
85
+ def herramientas(self, ident):
86
+ self.envia({'jsonrpc': '2.0', 'id': ident, 'method': 'tools/list'})
87
+ return self.espera(lambda m: m.get('id') == ident)
88
+
89
+ def herramienta(self, ident, nombre, argumentos=None):
90
+ self.envia({'jsonrpc': '2.0', 'id': ident, 'method': 'tools/call',
91
+ 'params': {'name': nombre, 'arguments': argumentos or {}}})
92
+ return self.espera(lambda m: m.get('id') == ident)
93
+
94
+ def cierra(self):
95
+ if self.p.poll() is None:
96
+ self.p.terminate()
97
+ try:
98
+ self.p.wait(timeout=2)
99
+ except subprocess.TimeoutExpired:
100
+ self.p.kill()
101
+
102
+
103
+ def texto(respuesta):
104
+ try:
105
+ return respuesta['result']['content'][0]['text']
106
+ except (KeyError, IndexError, TypeError):
107
+ return ''
108
+
109
+
110
+ def escribe_ciudad(ruta, ident, slug, repos, roads):
111
+ os.makedirs(ruta)
112
+ open(os.path.join(ruta, 'city.yml'), 'w', encoding='utf-8').write(
113
+ f'id: {ident}\nname: {slug.title()}\nslug: {slug}\nowner: alice\n')
114
+ open(os.path.join(ruta, 'alice.md'), 'w', encoding='utf-8').write(
115
+ f'---\nuser: alice\nagent: alice/ceo\nrepos: [{", ".join(repos)}]\n'
116
+ + ''.join(f'role.{repo}: seo\n' for repo in repos)
117
+ + '---\n')
118
+ open(os.path.join(ruta, 'roads.json'), 'w', encoding='utf-8').write(
119
+ json.dumps({'version': 1, 'roads': roads}) + '\n')
120
+
121
+
122
+ def detiene_hubs(app):
123
+ pids = []
124
+ for carpeta, _, nombres in os.walk(os.path.join(app, '.runtime', 'bus')):
125
+ if 'endpoint.json' not in nombres:
126
+ continue
127
+ try:
128
+ endpoint = json.load(open(os.path.join(carpeta, 'endpoint.json'),
129
+ encoding='utf-8'))
130
+ pids.append(int(endpoint['pid']))
131
+ except (OSError, ValueError, KeyError, json.JSONDecodeError):
132
+ pass
133
+ results = [detiene_proceso(pid) for pid in pids]
134
+ return all(results)
135
+
136
+
137
+ def main():
138
+ print('\n one typed bus, with native Claude Channel delivery')
139
+ if not shutil.which('node'):
140
+ afirma('· node is available for the shipped channel', False, 'node missing')
141
+ return resumen('channel')
142
+
143
+ base = tempfile.mkdtemp(prefix='agents-city-channel-')
144
+ app = os.path.join(base, 'app')
145
+ home = os.path.join(app, 'alice', 'home')
146
+ lab = os.path.join(app, 'alice', 'lab')
147
+ road_lab = {'id': 'city_lab', 'name': 'lab', 'owner': 'alice',
148
+ 'address': 'alice/lab', 'local': True}
149
+ road_home = {'id': 'city_home', 'name': 'home', 'owner': 'alice',
150
+ 'address': 'alice/home', 'local': True}
151
+ escribe_ciudad(home, 'city_home', 'home', ['api'], [road_lab])
152
+ escribe_ciudad(lab, 'city_lab', 'lab', [], [road_home])
153
+
154
+ standard = a = b = repo = None
155
+ try:
156
+ standard = Cliente('seat', home, app, channel=False)
157
+ afirma('· normal personal-account MCP does not advertise a custom Channel',
158
+ 'experimental' not in standard.inicial.get('result', {})
159
+ .get('capabilities', {}), str(standard.inicial))
160
+ standard.cierra()
161
+ standard = None
162
+ a = Cliente('seat', home, app)
163
+ lista = a.herramientas(2)
164
+ nombres = sorted(t['name'] for t in lista.get('result', {}).get('tools', []))
165
+ afirma('· MCP advertises the native Claude Channel capability',
166
+ bool(a.inicial.get('result'))
167
+ and a.inicial.get('result', {}).get('capabilities', {})
168
+ .get('experimental', {}).get('claude/channel') == {},
169
+ '\n'.join(a.err[-3:]))
170
+ endpoint_path = os.path.join(
171
+ app, '.runtime', 'bus', 'city-home', 'endpoint.json')
172
+ hub_started = espera(lambda: os.path.exists(endpoint_path))
173
+ endpoint = json.load(open(endpoint_path, encoding='utf-8')) if hub_started else {}
174
+ executable = subprocess.run(
175
+ ['ps', '-p', str(endpoint.get('pid', 0)), '-o', 'command='],
176
+ capture_output=True, text=True).stdout.strip() if endpoint else ''
177
+ afirma('· the production Channel launcher pins the WebSocket hub to Node',
178
+ hub_started and 'node' in executable.lower() and 'local-hub.js' in executable,
179
+ executable)
180
+ comprueba('· the authenticated seat exposes the road trio', nombres,
181
+ ['bus_inbox', 'bus_roster', 'bus_send'])
182
+
183
+ repo = Cliente('api', home, app)
184
+ afirma(
185
+ '· the repo MCP context names its operating role without chair authority',
186
+ 'operating role is seo' in repo.inicial.get('result', {}).get('instructions', ''),
187
+ str(repo.inicial),
188
+ )
189
+ repo_tools = repo.herramientas(20)
190
+ comprueba('· a repo runtime receives no road capability',
191
+ repo_tools.get('result', {}).get('tools', []), [])
192
+
193
+ opened = subprocess.run(
194
+ ['node', CLIENT, 'committee', 'open', '--input', '-'],
195
+ input=json.dumps({
196
+ 'question': 'Check the API contract',
197
+ 'desiredOutcome': 'One evidence-backed position',
198
+ 'definitionOfDone': ['the repo submits its evidence'],
199
+ 'participants': ['api'],
200
+ }), capture_output=True, text=True, env=dict(
201
+ os.environ, CITY_BUS_ACTOR='seat', AGENTS_CITY_DATA=home,
202
+ AGENTS_CITY_HOME=app, AGENTS_CITY_USER='alice'), timeout=12)
203
+ state = json.loads(opened.stdout) if opened.returncode == 0 else {}
204
+ thread = state.get('id', '')
205
+ notice = repo.espera(
206
+ lambda m: m.get('method') == 'notifications/claude/channel'
207
+ and m.get('params', {}).get('meta', {}).get('thread') == thread)
208
+ content = notice.get('params', {}).get('content', '')
209
+ afirma('· a repo assignment arrives as a native channel notification',
210
+ bool(thread) and thread in content
211
+ and 'agents-city committee respond' in content,
212
+ opened.stderr.strip() or str(notice))
213
+ meta = notice.get('params', {}).get('meta', {})
214
+ afirma('· channel metadata preserves the authenticated envelope identity',
215
+ meta.get('actor') == 'api'
216
+ and meta.get('envelope_id', '').startswith('msg_'),
217
+ str(notice))
218
+ outbox = os.path.join(app, '.runtime', 'bus', 'city-home', 'outbox', 'api')
219
+ for _ in range(50):
220
+ if not os.path.isdir(outbox) or not os.listdir(outbox):
221
+ break
222
+ time.sleep(.05)
223
+ afirma('· the durable outbox drains only after Channel accepts the event',
224
+ not os.path.isdir(outbox) or not os.listdir(outbox))
225
+
226
+ # Non-happy path: Claude stays open while its local WebSocket hub is
227
+ # forcibly stopped. The next command recreates the hub; the Channel
228
+ # reconnects, receives the durable assignment once, and ACKs it.
229
+ afirma('· the forced outage stops Claude\'s shared WebSocket hub',
230
+ detiene_proceso(int(endpoint['pid'])))
231
+ afirma('· the stale Claude hub endpoint disappears',
232
+ espera(lambda: not os.path.exists(endpoint_path)))
233
+ reopened = subprocess.run(
234
+ ['node', CLIENT, 'committee', 'open', '--input', '-'],
235
+ input=json.dumps({
236
+ 'question': 'Prove Claude Channel recovery after a bus outage.',
237
+ 'desiredOutcome': 'One native Channel delivery after reconnect.',
238
+ 'definitionOfDone': ['the api actor receives the retained assignment'],
239
+ 'participants': ['api'],
240
+ }), capture_output=True, text=True, env=dict(
241
+ os.environ, CITY_BUS_ACTOR='seat', AGENTS_CITY_DATA=home,
242
+ AGENTS_CITY_HOME=app, AGENTS_CITY_USER='alice'), timeout=12)
243
+ recovery = json.loads(reopened.stdout) if reopened.returncode == 0 else {}
244
+ recovery_thread = recovery.get('id', '')
245
+ recovered_notice = repo.espera(
246
+ lambda m: m.get('method') == 'notifications/claude/channel'
247
+ and m.get('params', {}).get('meta', {}).get('thread') == recovery_thread,
248
+ segundos=12)
249
+ afirma('· Claude Channel reconnects and receives the retained assignment',
250
+ bool(recovery_thread) and bool(recovered_notice),
251
+ reopened.stderr.strip() or str(recovered_notice))
252
+ afirma('· Claude recovery ACKs its durable outbox',
253
+ espera(lambda: not os.path.isdir(outbox) or not os.listdir(outbox)))
254
+ time.sleep(.35)
255
+ recovery_notices = [
256
+ m for m in repo.mensajes
257
+ if m.get('method') == 'notifications/claude/channel'
258
+ and m.get('params', {}).get('meta', {}).get('thread') == recovery_thread
259
+ ]
260
+ comprueba('· Claude recovery injects the assignment exactly once',
261
+ len(recovery_notices), 1)
262
+ afirma('· Claude outage recovery never creates a terminal adapter',
263
+ not os.path.exists(os.path.join(
264
+ app, '.runtime', 'bus', 'city-home', 'adapters')))
265
+
266
+ # Non-happy path: an actor can be offline while a committee both opens
267
+ # and reaches a terminal state. Reconnecting must drain those obsolete
268
+ # durable envelopes without waking the model with work that no longer
269
+ # exists (the exact failure that made a closed committee restart).
270
+ repo.cierra()
271
+ repo = None
272
+ stale_marker = 'STALE_CANCELLED_ASSIGNMENT_MUST_NOT_RUN'
273
+ stale_opened = subprocess.run(
274
+ ['node', CLIENT, 'committee', 'open', '--input', '-'],
275
+ input=json.dumps({
276
+ 'question': stale_marker,
277
+ 'desiredOutcome': 'Nothing reaches an agent after cancellation.',
278
+ 'definitionOfDone': ['the offline outbox is discarded on reconnect'],
279
+ 'participants': ['api'],
280
+ }), capture_output=True, text=True, env=dict(
281
+ os.environ, CITY_BUS_ACTOR='seat', AGENTS_CITY_DATA=home,
282
+ AGENTS_CITY_HOME=app, AGENTS_CITY_USER='alice'), timeout=12)
283
+ stale_state = json.loads(stale_opened.stdout) if stale_opened.returncode == 0 else {}
284
+ stale_thread = stale_state.get('id', '')
285
+ cancelled = subprocess.run(
286
+ ['node', CLIENT, 'committee', 'cancel', stale_thread,
287
+ '--reason', 'test cancellation while participant is offline'],
288
+ capture_output=True, text=True, env=dict(
289
+ os.environ, CITY_BUS_ACTOR='seat', AGENTS_CITY_DATA=home,
290
+ AGENTS_CITY_HOME=app, AGENTS_CITY_USER='alice'), timeout=12)
291
+ afirma('· non-happy: terminal work remains durably queued while the agent is offline',
292
+ bool(stale_thread) and cancelled.returncode == 0
293
+ and os.path.isdir(outbox) and len(os.listdir(outbox)) == 2,
294
+ cancelled.stderr or str(os.listdir(outbox) if os.path.isdir(outbox) else []))
295
+ repo = Cliente('api', home, app)
296
+ afirma('· non-happy: reconnect discards every obsolete terminal delivery',
297
+ espera(lambda: not os.path.isdir(outbox) or not os.listdir(outbox)),
298
+ str(os.listdir(outbox) if os.path.isdir(outbox) else []))
299
+ time.sleep(.25)
300
+ stale_notices = [
301
+ m for m in repo.mensajes
302
+ if m.get('method') == 'notifications/claude/channel'
303
+ and m.get('params', {}).get('meta', {}).get('thread') == stale_thread
304
+ ]
305
+ comprueba('· non-happy: a cancelled committee never wakes the provider after reconnect',
306
+ stale_notices, [])
307
+ diagnostics_path = os.path.join(
308
+ app, '.runtime', 'bus', 'city-home', 'diagnostics.jsonl')
309
+ diagnostics = open(diagnostics_path, encoding='utf-8').read()
310
+ afirma('· stale drops are durable and contain no committee prompt',
311
+ diagnostics.count('"event":"delivery.stale.dropped"') >= 2
312
+ and stale_thread in diagnostics
313
+ and stale_marker not in diagnostics,
314
+ diagnostics[-1200:])
315
+
316
+ fuera = a.herramienta(3, 'bus_send',
317
+ {'to': 'alice/ghost', 'text': 'not on a road'})
318
+ afirma('· a city cannot send outside its explicit roads',
319
+ fuera.get('result', {}).get('isError') is True
320
+ and 'no road' in texto(fuera), texto(fuera))
321
+
322
+ enviado = a.herramienta(4, 'bus_send',
323
+ {'to': 'alice/lab', 'text': 'hello from home'})
324
+ afirma('· an offline city is durably queued by the same hub',
325
+ not enviado.get('result', {}).get('isError')
326
+ and 'queued on the local bus' in texto(enviado), texto(enviado))
327
+ queued_dir = os.path.join(app, '.runtime', 'bus', 'city-lab', 'road-queue')
328
+ queued_file = os.path.join(queued_dir, os.listdir(queued_dir)[0])
329
+ comprueba('· queue directory and envelope are private',
330
+ (os.stat(queued_dir).st_mode & 0o777,
331
+ os.stat(queued_file).st_mode & 0o777), (0o700, 0o600))
332
+ envelope = json.load(open(queued_file, encoding='utf-8'))
333
+ afirma('· the queue carries the shared typed envelope',
334
+ envelope.get('protocol') == 'agents-city-bus/2'
335
+ and envelope.get('scope') == 'road'
336
+ and envelope.get('from', {}).get('actor') == 'seat'
337
+ and envelope.get('to', {}).get('actor') == 'seat')
338
+ grande = a.herramienta(8, 'bus_send',
339
+ {'to': 'alice/lab', 'text': 'x' * 64_001})
340
+ afirma('· local roads enforce the relay size boundary',
341
+ grande.get('result', {}).get('isError') is True
342
+ and 'too large' in texto(grande), texto(grande))
343
+
344
+ b = Cliente('seat', lab, app)
345
+ inbox = b.herramienta(5, 'bus_inbox')
346
+ afirma('· starting the destination drains the durable road queue',
347
+ 'hello from home' in texto(inbox)
348
+ and 'agents-city-bus/2' in texto(inbox), texto(inbox))
349
+ vacio = b.herramienta(6, 'bus_inbox')
350
+ afirma('· reading the road inbox clears it',
351
+ 'nothing new' in texto(vacio).lower(), texto(vacio))
352
+ roster = b.herramienta(7, 'bus_roster')
353
+ afirma('· roster is road-scoped and sees the other local hub online',
354
+ 'alice/home' in texto(roster)
355
+ and 'alice/ghost' not in texto(roster)
356
+ and '"online": true' in texto(roster), texto(roster))
357
+ road_notices = [m for m in b.mensajes
358
+ if m.get('method') == 'notifications/claude/channel']
359
+ afirma('· a queued road event reaches the seat through Channel exactly once',
360
+ len(road_notices) == 1
361
+ and 'hello from home' in road_notices[0].get('params', {}).get('content', ''),
362
+ str(road_notices))
363
+ before = len(road_notices)
364
+ b.herramienta(9, 'bus_roster')
365
+ time.sleep(.15)
366
+ after = len([m for m in b.mensajes
367
+ if m.get('method') == 'notifications/claude/channel'])
368
+ afirma('· opening MCP status never duplicates a native prompt', before == after)
369
+ finally:
370
+ for cliente in (standard, a, b, repo):
371
+ if cliente:
372
+ cliente.cierra()
373
+ afirma('· Channel cleanup leaves no orphan city hub', detiene_hubs(app))
374
+ shutil.rmtree(base, ignore_errors=True)
375
+ return resumen('channel')
376
+
377
+
378
+ if __name__ == '__main__':
379
+ sys.exit(main())