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,453 @@
1
+ #!/usr/bin/env python3
2
+ """Personal cities: ownership, migration, roads, reset and live skills.
3
+
4
+ Everything runs below a throwaway HOME. These checks are deliberately about
5
+ boundaries: creating a second city must not mutate the first, a reset must not
6
+ touch a repo, and skill recognition must remain read-only.
7
+ """
8
+
9
+ import contextlib
10
+ import hashlib
11
+ import os
12
+ import shutil
13
+ import subprocess
14
+ import sys
15
+ import tempfile
16
+ from types import SimpleNamespace
17
+
18
+ AQUI = os.path.dirname(os.path.abspath(__file__))
19
+ RAIZ = os.path.dirname(AQUI)
20
+ sys.path.insert(0, os.path.join(RAIZ, "plugin", "scripts"))
21
+ sys.path.insert(0, AQUI)
22
+
23
+ import capabilities # noqa: E402
24
+ import cities # noqa: E402
25
+ import reset as city_reset # noqa: E402
26
+ import roads # noqa: E402
27
+ import runtime_processes # noqa: E402
28
+ from testlib import afirma, comprueba, resumen # noqa: E402
29
+
30
+
31
+ @contextlib.contextmanager
32
+ def aislado():
33
+ base = tempfile.mkdtemp(prefix="agents-city-v2-")
34
+ casa = os.path.join(base, "home")
35
+ raiz_ciudades = os.path.join(base, "app")
36
+ os.makedirs(casa)
37
+ claves = ("HOME", "AGENTS_CITY_HOME", "AGENTS_CITY_USER", "AGENTS_CITY_DATA", "CITY_CODE_DIR")
38
+ previo = {k: os.environ.get(k) for k in claves}
39
+ registro_previo = cities.REGISTRO
40
+ os.environ.update(
41
+ {
42
+ "HOME": casa,
43
+ "AGENTS_CITY_HOME": raiz_ciudades,
44
+ "AGENTS_CITY_USER": "alice",
45
+ }
46
+ )
47
+ os.environ.pop("AGENTS_CITY_DATA", None)
48
+ cities.REGISTRO = os.path.join(base, "config", "cities")
49
+ try:
50
+ yield base, raiz_ciudades
51
+ finally:
52
+ cities.REGISTRO = registro_previo
53
+ for clave, valor in previo.items():
54
+ if valor is None:
55
+ os.environ.pop(clave, None)
56
+ else:
57
+ os.environ[clave] = valor
58
+ shutil.rmtree(base, ignore_errors=True)
59
+
60
+
61
+ def arbol(ruta):
62
+ """Names and content hashes, without timestamps or access metadata."""
63
+ salida = []
64
+ if not os.path.exists(ruta):
65
+ return salida
66
+ for base, dirs, files in os.walk(ruta):
67
+ dirs.sort()
68
+ files.sort()
69
+ relbase = os.path.relpath(base, ruta)
70
+ salida.extend(("d", os.path.join(relbase, d)) for d in dirs)
71
+ for nombre in files:
72
+ fichero = os.path.join(base, nombre)
73
+ contenido = open(fichero, "rb").read()
74
+ salida.append(
75
+ ("f", os.path.join(relbase, nombre), hashlib.sha256(contenido).hexdigest())
76
+ )
77
+ return salida
78
+
79
+
80
+ def identidad_y_catalogo():
81
+ print(" one user owns several autonomous cities")
82
+ with aislado() as (_, app):
83
+ home = cities.actual("alice")
84
+ comprueba(
85
+ "· first city is nested below the user",
86
+ home,
87
+ os.path.realpath(os.path.join(app, "alice", "home")),
88
+ )
89
+ afirma("· the application root is only a container", not cities.es_ciudad(app))
90
+ afirma(
91
+ "· identity metadata exists from the first byte",
92
+ cities.identidad(home).startswith("city_")
93
+ and cities.lee_clave(home, "layout") == "personal-v2",
94
+ )
95
+ comprueba(
96
+ "· the first address names owner and city",
97
+ cities.direccion("alice", home),
98
+ "alice/home",
99
+ )
100
+ comprueba(
101
+ "· even home has a collision-proof tmux session",
102
+ cities.sesion("alice", home),
103
+ "alice-home",
104
+ )
105
+
106
+ taller = cities.crea("alice", "Taller Norte")
107
+ comprueba(
108
+ "· a second city is another child of the same user",
109
+ taller,
110
+ os.path.realpath(os.path.join(app, "alice", "taller-norte")),
111
+ )
112
+ comprueba(
113
+ "· selecting it does not erase home",
114
+ (cities.actual("alice"), os.path.isdir(home)),
115
+ (taller, True),
116
+ )
117
+ comprueba(
118
+ "· both are independently listed",
119
+ {c["slug"] for c in cities.lista("alice")},
120
+ {"home", "taller-norte"},
121
+ )
122
+
123
+ ajena = os.path.join(app, "alice", "foreign")
124
+ cities.asegura_metadata(ajena, "bob", "foreign")
125
+ afirma(
126
+ "· a folder owned by somebody else is not one of this user cities",
127
+ "foreign" not in {c["slug"] for c in cities.lista("alice")},
128
+ )
129
+
130
+ ident = cities.identidad(taller)
131
+ estado = cities.slug(taller)
132
+ movida = os.path.join(os.path.dirname(app), "moved-city")
133
+ shutil.move(taller, movida)
134
+ comprueba(
135
+ "· moving a city keeps its stable identity",
136
+ (cities.identidad(movida), cities.slug(movida)),
137
+ (ident, estado),
138
+ )
139
+
140
+
141
+ def clave_de_ciudad():
142
+ """One reader, one writer for city.yml scalars — seat_yolo's home."""
143
+ print(" one scalar key, read and written by the one owner")
144
+ with aislado() as (_, _app):
145
+ home = cities.actual("alice")
146
+ # Happy: absent reads empty, an upsert lands, a second upsert replaces
147
+ # in place instead of appending a duplicate line.
148
+ comprueba("· an unset key reads as empty", cities.lee_clave(home, "seat_yolo"), "")
149
+ cities.pon_clave(home, "seat_yolo", "1")
150
+ comprueba("· the upsert lands", cities.lee_clave(home, "seat_yolo"), "1")
151
+ cities.pon_clave(home, "seat_yolo", "0")
152
+ texto = open(os.path.join(home, "city.yml"), encoding="utf-8").read()
153
+ afirma(
154
+ "· changing it replaces the line, never duplicates it",
155
+ cities.lee_clave(home, "seat_yolo") == "0" and texto.count("seat_yolo:") == 1,
156
+ texto,
157
+ )
158
+ afirma(
159
+ "· the identity around it survives the write",
160
+ cities.lee_clave(home, "layout") == "personal-v2" and cities.identidad(home),
161
+ )
162
+ # The CLI speaks the same key, both directions.
163
+ entorno = dict(os.environ)
164
+ guion = os.path.join(RAIZ, "plugin", "scripts", "cities.py")
165
+ r = subprocess.run(
166
+ [sys.executable, guion, "clave", home, "seat_yolo"],
167
+ capture_output=True,
168
+ text=True,
169
+ env=entorno,
170
+ )
171
+ comprueba("· the CLI reads it", r.stdout.strip(), "0")
172
+ subprocess.run(
173
+ [sys.executable, guion, "clave", home, "seat_yolo", "1"],
174
+ capture_output=True,
175
+ text=True,
176
+ env=entorno,
177
+ )
178
+ comprueba("· and writes it", cities.lee_clave(home, "seat_yolo"), "1")
179
+ # Non-happy: a malformed call is usage, not a traceback or a write.
180
+ r = subprocess.run(
181
+ [sys.executable, guion, "clave", home],
182
+ capture_output=True,
183
+ text=True,
184
+ env=entorno,
185
+ )
186
+ afirma(
187
+ "· non-happy: a malformed clave call is usage, never a stack trace",
188
+ r.returncode != 0 and "Traceback" not in r.stderr,
189
+ r.stderr,
190
+ )
191
+
192
+ # The launcher consumes the key and routes it into the seat's own line: a
193
+ # source contract, because the seat launch itself needs a live tmux.
194
+ lanzador = open(
195
+ os.path.join(RAIZ, "plugin", "scripts", "city-session.sh"), encoding="utf-8"
196
+ ).read()
197
+ afirma(
198
+ "· city-session reads seat_yolo and arms the seat with it",
199
+ "cities.py\" clave \"$EQUIPO\" seat_yolo" in lanzador.replace("'", '"')
200
+ or "clave \"$EQUIPO\" seat_yolo" in lanzador,
201
+ "",
202
+ )
203
+ afirma(
204
+ "· the seat line carries the flag and the gateway auto-approve together",
205
+ "$SEAT_YOLO_FLAG" in lanzador and '"$SEAT_AUTO"' in lanzador,
206
+ "",
207
+ )
208
+ afirma(
209
+ "· --no-yolo still brakes the seat too",
210
+ '[ "$YOLO" -eq 0 ] && SEAT_YOLO=0' in lanzador,
211
+ "",
212
+ )
213
+
214
+
215
+ def migracion_v1():
216
+ print(" the old flat layout migrates recoverably")
217
+ with aislado() as (_, app):
218
+ os.makedirs(app)
219
+ open(os.path.join(app, "city.yml"), "w").write("kind: product\n")
220
+ open(os.path.join(app, "alice.md"), "w").write(
221
+ "---\nuser: alice\nrole: dev\nagent: alice/dev\nrepos: [repo]\n---\n"
222
+ )
223
+ os.makedirs(os.path.join(app, "roles"))
224
+ open(os.path.join(app, "roles", "dev.md"), "w").write("# Dev\n")
225
+ os.makedirs(os.path.join(app, "state"))
226
+ open(os.path.join(app, "state", "db"), "w").write("runtime-state")
227
+ # Proves an interrupted v2 container is never moved below itself.
228
+ os.makedirs(os.path.join(app, "alice"))
229
+
230
+ destino = cities.migra_legacy("alice")
231
+ comprueba(
232
+ "· flat data lands in user/home",
233
+ destino,
234
+ os.path.realpath(os.path.join(app, "alice", "home")),
235
+ )
236
+ afirma(
237
+ "· every city file moved",
238
+ os.path.isfile(os.path.join(destino, "alice.md"))
239
+ and os.path.isfile(os.path.join(destino, "roles", "dev.md")),
240
+ )
241
+ comprueba(
242
+ "· runtime state stays at application scope",
243
+ open(os.path.join(app, "state", "db")).read(),
244
+ "runtime-state",
245
+ )
246
+ copias = [
247
+ os.path.join(app, ".backups", n) for n in os.listdir(os.path.join(app, ".backups"))
248
+ ]
249
+ afirma(
250
+ "· a complete recovery copy exists before the move",
251
+ len(copias) == 1
252
+ and os.path.isfile(os.path.join(copias[0], "alice.md"))
253
+ and os.path.isfile(os.path.join(copias[0], "roles", "dev.md")),
254
+ )
255
+ comprueba("· migration is idempotent", cities.migra_legacy("alice"), "")
256
+ comprueba(
257
+ "· the migrated role address becomes the city address",
258
+ cities.direccion("alice", destino),
259
+ "alice/home",
260
+ )
261
+
262
+
263
+ def carreteras():
264
+ print(" roads connect cities, not repo agents")
265
+ with aislado():
266
+ home = cities.actual("alice")
267
+ lab = cities.crea("alice", "lab")
268
+ roads.conecta(home, lab, "alice")
269
+ a, b = roads.lee(home), roads.lee(lab)
270
+ comprueba(
271
+ "· a local road is written at both ends",
272
+ (a[0]["id"], b[0]["id"]),
273
+ (cities.identidad(lab), cities.identidad(home)),
274
+ )
275
+ comprueba(
276
+ "· endpoints are city seats",
277
+ (a[0]["address"], b[0]["address"]),
278
+ ("alice/lab", "alice/home"),
279
+ )
280
+ afirma("· local transport is explicit", a[0].get("local") and b[0].get("local"))
281
+
282
+ invite = roads.invitacion(lab, "alice")
283
+ afirma(
284
+ "· a public invitation carries routing context but never credentials",
285
+ {"version", "id", "name", "owner", "address", "domain"} <= set(invite)
286
+ and invite["domain"] == "software"
287
+ and not any("token" in k.lower() for k in invite),
288
+ )
289
+ try:
290
+ roads.conecta(home, home, "alice")
291
+ autocamino = True
292
+ except ValueError:
293
+ autocamino = False
294
+ afirma("· a city cannot connect to itself", not autocamino)
295
+ con_secreto = dict(invite, token="must-not-land")
296
+ try:
297
+ roads.conecta_invitacion(home, con_secreto)
298
+ secreto_rechazado = False
299
+ except ValueError:
300
+ secreto_rechazado = True
301
+ afirma(
302
+ "· an invitation with non-public fields is refused",
303
+ secreto_rechazado and len(roads.lee(home)) == 1,
304
+ )
305
+ mismo_address = dict(invite, id="city_different", address="alice/home", owner="alice")
306
+ try:
307
+ roads.conecta_invitacion(home, mismo_address)
308
+ colision_rechazada = False
309
+ except ValueError:
310
+ colision_rechazada = True
311
+ afirma("· a different id cannot reuse this city address", colision_rechazada)
312
+ afirma(
313
+ "· disconnecting a local road removes both halves",
314
+ roads.desconecta_local(home, lab) and roads.lee(home) == [] and roads.lee(lab) == [],
315
+ )
316
+
317
+
318
+ def reinicio_aislado():
319
+ print(" reset affects exactly one city and no repo")
320
+ with aislado() as (base, app):
321
+ home = cities.actual("alice")
322
+ lab = cities.crea("alice", "lab")
323
+ roads.conecta(home, lab, "alice")
324
+ open(os.path.join(lab, "alice.md"), "w").write(
325
+ "---\nuser: alice\nrole: dev\nagent: alice/lab\nrepos: [repo]\n---\n"
326
+ )
327
+ repo = os.path.join(base, "repo")
328
+ os.makedirs(repo)
329
+ open(os.path.join(repo, "important.txt"), "w").write("never touch this")
330
+ estado = os.path.join(app, "state", cities.slug(lab))
331
+ os.makedirs(estado)
332
+ open(os.path.join(estado, "db"), "w").write("map state")
333
+ runtime = runtime_processes.ruta(lab)
334
+ os.makedirs(runtime)
335
+ open(os.path.join(runtime, "endpoint.json"), "w").write("runtime marker")
336
+
337
+ city_reset._deten = lambda *_args, **_kw: SimpleNamespace(
338
+ stdout="", stderr="", returncode=0
339
+ )
340
+ antes = arbol(app)
341
+ city_reset.reinicia(lab, "alice", dry_run=True)
342
+ comprueba("· dry-run changes no byte", arbol(app), antes)
343
+
344
+ city_reset._deten = lambda *_args, **_kw: SimpleNamespace(
345
+ stdout="", stderr="tmux refused", returncode=1
346
+ )
347
+ try:
348
+ city_reset.reinicia(lab, "alice")
349
+ parada_segura = False
350
+ except ValueError:
351
+ parada_segura = True
352
+ afirma(
353
+ "· reset aborts before moving data when the city cannot stop",
354
+ parada_segura and arbol(app) == antes,
355
+ )
356
+ city_reset._deten = lambda *_args, **_kw: SimpleNamespace(
357
+ stdout="", stderr="", returncode=0
358
+ )
359
+
360
+ ident = cities.identidad(lab)
361
+ backup = city_reset.reinicia(lab, "alice")
362
+ comprueba("· the empty replacement keeps the city identity", cities.identidad(lab), ident)
363
+ afirma(
364
+ "· onboarding state has no seat and no road",
365
+ not os.path.exists(os.path.join(lab, "alice.md")) and roads.lee(lab) == [],
366
+ )
367
+ afirma(
368
+ "· the other endpoint is cleaned but its city survives",
369
+ os.path.isdir(home) and roads.lee(home) == [],
370
+ )
371
+ comprueba(
372
+ "· the referenced repo is byte-for-byte untouched",
373
+ open(os.path.join(repo, "important.txt")).read(),
374
+ "never touch this",
375
+ )
376
+ afirma(
377
+ "· both city data and map state are recoverable",
378
+ os.path.isfile(os.path.join(backup, "city", "alice.md"))
379
+ and os.path.isfile(os.path.join(backup, "state", "db")),
380
+ )
381
+ comprueba(
382
+ "· stopped bus state is recoverable too",
383
+ open(os.path.join(backup, "runtime", "endpoint.json")).read(),
384
+ "runtime marker",
385
+ )
386
+
387
+ externa = os.path.join(base, "shared")
388
+ cities.asegura_metadata(externa, "alice", "shared")
389
+ try:
390
+ city_reset.reinicia(externa, "alice")
391
+ rechazo = False
392
+ except ValueError:
393
+ rechazo = True
394
+ afirma("· reset refuses external or shared folders", rechazo)
395
+
396
+ ajena = os.path.join(app, "alice", "foreign")
397
+ cities.asegura_metadata(ajena, "bob", "foreign")
398
+ try:
399
+ city_reset.reinicia(ajena, "alice")
400
+ rechazo_owner = False
401
+ except ValueError:
402
+ rechazo_owner = True
403
+ afirma("· reset refuses a city owned by another identity", rechazo_owner)
404
+
405
+
406
+ def skills_transparentes():
407
+ print(" repo skills stay owned by each repo runtime")
408
+ with aislado() as (base, _):
409
+ ciudad = cities.actual("alice")
410
+ open(os.path.join(ciudad, "alice.md"), "w").write(
411
+ "---\nuser: alice\nrole: dev\nagent: alice/home\n"
412
+ "repos: [api]\ngoals_defined: false\n---\n"
413
+ )
414
+ repo = os.path.join(base, "api")
415
+ skill = os.path.join(repo, ".codex", "skills", "deploy")
416
+ os.makedirs(skill)
417
+ open(os.path.join(skill, "SKILL.md"), "w").write(
418
+ "---\nname: safe-deploy\ndescription: Deploy this API safely.\n---\n"
419
+ )
420
+ antes = arbol(repo)
421
+ vistas = capabilities.descubre_ciudad(ciudad, {"api": repo})
422
+ despues = arbol(repo)
423
+ comprueba(
424
+ "· the installed repo skill is recognised live",
425
+ [s["name"] for s in vistas["api"]["skills"]],
426
+ ["safe-deploy"],
427
+ )
428
+ comprueba("· recognition installs, copies and edits nothing", despues, antes)
429
+
430
+ otra = os.path.join(repo, "skills", "audit")
431
+ os.makedirs(otra)
432
+ open(os.path.join(otra, "SKILL.md"), "w").write(
433
+ "---\nname: audit\ndescription: Inspect evidence.\n---\n"
434
+ )
435
+ nombres = [
436
+ s["name"] for s in capabilities.descubre_ciudad(ciudad, {"api": repo})["api"]["skills"]
437
+ ]
438
+ comprueba("· there is no stale capability cache", nombres, ["safe-deploy", "audit"])
439
+
440
+
441
+ def main():
442
+ print()
443
+ identidad_y_catalogo()
444
+ clave_de_ciudad()
445
+ migracion_v1()
446
+ carreteras()
447
+ reinicio_aislado()
448
+ skills_transparentes()
449
+ return resumen("cities")
450
+
451
+
452
+ if __name__ == "__main__":
453
+ sys.exit(main())