nexakit 0.0.0-stage → 0.2.0

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 (438) hide show
  1. package/.claude-plugin/marketplace.json +28 -0
  2. package/LICENSE +26 -0
  3. package/README.md +79 -2
  4. package/agents/claude/docs-researcher.md +9 -0
  5. package/agents/claude/implementation-tutor.md +9 -0
  6. package/agents/claude/project-explorer.md +9 -0
  7. package/agents/claude/quality-reviewer.md +9 -0
  8. package/agents/claude/requirements-analyst.md +9 -0
  9. package/agents/claude/solution-architect.md +9 -0
  10. package/agents/codex/docs-researcher.toml +10 -0
  11. package/agents/codex/implementation-tutor.toml +10 -0
  12. package/agents/codex/project-explorer.toml +10 -0
  13. package/agents/codex/quality-reviewer.toml +10 -0
  14. package/agents/codex/requirements-analyst.toml +11 -0
  15. package/agents/codex/solution-architect.toml +10 -0
  16. package/assets/brand/README.md +38 -0
  17. package/assets/brand/agent-toolkit-mark.png +0 -0
  18. package/assets/brand/agent-toolkit-mark.svg +13 -0
  19. package/assets/brand/agent-toolkit-tui-hero.png +0 -0
  20. package/assets/brand/nexakit-readme-hero.png +0 -0
  21. package/assets/brand/nexakit-readme-hero.svg +21 -0
  22. package/assets/runtime/package-lock.json +1425 -0
  23. package/bin/agent-task.js +14 -0
  24. package/bin/agent-toolkit-gemini.js +13 -0
  25. package/bin/agent-toolkit-inbox.js +14 -0
  26. package/bin/agent-toolkit-mcp.js +17 -0
  27. package/bin/agent-toolkit.js +13 -0
  28. package/bin/nexakit.js +13 -0
  29. package/bin/project-memory-mcp.js +22 -0
  30. package/bin/project-memory.js +7 -0
  31. package/catalogs/skills/api-design-principles/LICENSE.txt +21 -0
  32. package/catalogs/skills/api-design-principles/SKILL.md +110 -0
  33. package/catalogs/skills/api-design-principles/assets/api-design-checklist.md +155 -0
  34. package/catalogs/skills/api-design-principles/assets/rest-api-template.py +185 -0
  35. package/catalogs/skills/api-design-principles/references/details.md +413 -0
  36. package/catalogs/skills/api-design-principles/references/graphql-schema-design.md +589 -0
  37. package/catalogs/skills/api-design-principles/references/rest-best-practices.md +408 -0
  38. package/catalogs/skills/nodejs-backend-patterns/LICENSE.txt +21 -0
  39. package/catalogs/skills/nodejs-backend-patterns/SKILL.md +45 -0
  40. package/catalogs/skills/nodejs-backend-patterns/references/advanced-patterns.md +430 -0
  41. package/catalogs/skills/nodejs-backend-patterns/references/details.md +599 -0
  42. package/catalogs/skills/supabase-postgres-best-practices/CHANGELOG.md +73 -0
  43. package/catalogs/skills/supabase-postgres-best-practices/LICENSE.txt +21 -0
  44. package/catalogs/skills/supabase-postgres-best-practices/SKILL.md +64 -0
  45. package/catalogs/skills/supabase-postgres-best-practices/references/_contributing.md +170 -0
  46. package/catalogs/skills/supabase-postgres-best-practices/references/_sections.md +39 -0
  47. package/catalogs/skills/supabase-postgres-best-practices/references/_template.md +34 -0
  48. package/catalogs/skills/supabase-postgres-best-practices/references/advanced-full-text-search.md +55 -0
  49. package/catalogs/skills/supabase-postgres-best-practices/references/advanced-jsonb-indexing.md +49 -0
  50. package/catalogs/skills/supabase-postgres-best-practices/references/conn-idle-timeout.md +46 -0
  51. package/catalogs/skills/supabase-postgres-best-practices/references/conn-limits.md +44 -0
  52. package/catalogs/skills/supabase-postgres-best-practices/references/conn-pooling.md +41 -0
  53. package/catalogs/skills/supabase-postgres-best-practices/references/conn-prepared-statements.md +46 -0
  54. package/catalogs/skills/supabase-postgres-best-practices/references/data-batch-inserts.md +54 -0
  55. package/catalogs/skills/supabase-postgres-best-practices/references/data-n-plus-one.md +53 -0
  56. package/catalogs/skills/supabase-postgres-best-practices/references/data-pagination.md +50 -0
  57. package/catalogs/skills/supabase-postgres-best-practices/references/data-upsert.md +50 -0
  58. package/catalogs/skills/supabase-postgres-best-practices/references/lock-advisory.md +56 -0
  59. package/catalogs/skills/supabase-postgres-best-practices/references/lock-deadlock-prevention.md +68 -0
  60. package/catalogs/skills/supabase-postgres-best-practices/references/lock-short-transactions.md +50 -0
  61. package/catalogs/skills/supabase-postgres-best-practices/references/lock-skip-locked.md +54 -0
  62. package/catalogs/skills/supabase-postgres-best-practices/references/monitor-explain-analyze.md +45 -0
  63. package/catalogs/skills/supabase-postgres-best-practices/references/monitor-pg-stat-statements.md +55 -0
  64. package/catalogs/skills/supabase-postgres-best-practices/references/monitor-vacuum-analyze.md +55 -0
  65. package/catalogs/skills/supabase-postgres-best-practices/references/query-composite-indexes.md +44 -0
  66. package/catalogs/skills/supabase-postgres-best-practices/references/query-covering-indexes.md +40 -0
  67. package/catalogs/skills/supabase-postgres-best-practices/references/query-index-types.md +48 -0
  68. package/catalogs/skills/supabase-postgres-best-practices/references/query-missing-indexes.md +43 -0
  69. package/catalogs/skills/supabase-postgres-best-practices/references/query-partial-indexes.md +45 -0
  70. package/catalogs/skills/supabase-postgres-best-practices/references/schema-constraints.md +80 -0
  71. package/catalogs/skills/supabase-postgres-best-practices/references/schema-data-types.md +46 -0
  72. package/catalogs/skills/supabase-postgres-best-practices/references/schema-foreign-key-indexes.md +59 -0
  73. package/catalogs/skills/supabase-postgres-best-practices/references/schema-lowercase-identifiers.md +55 -0
  74. package/catalogs/skills/supabase-postgres-best-practices/references/schema-partitioning.md +55 -0
  75. package/catalogs/skills/supabase-postgres-best-practices/references/schema-primary-keys.md +61 -0
  76. package/catalogs/skills/supabase-postgres-best-practices/references/security-privileges.md +54 -0
  77. package/catalogs/skills/supabase-postgres-best-practices/references/security-rls-basics.md +50 -0
  78. package/catalogs/skills/supabase-postgres-best-practices/references/security-rls-performance.md +63 -0
  79. package/catalogs/skills/vercel-composition-patterns/AGENTS.md +946 -0
  80. package/catalogs/skills/vercel-composition-patterns/LICENSE.txt +27 -0
  81. package/catalogs/skills/vercel-composition-patterns/README.md +60 -0
  82. package/catalogs/skills/vercel-composition-patterns/SKILL.md +89 -0
  83. package/catalogs/skills/vercel-composition-patterns/metadata.json +11 -0
  84. package/catalogs/skills/vercel-composition-patterns/rules/_sections.md +29 -0
  85. package/catalogs/skills/vercel-composition-patterns/rules/_template.md +24 -0
  86. package/catalogs/skills/vercel-composition-patterns/rules/architecture-avoid-boolean-props.md +100 -0
  87. package/catalogs/skills/vercel-composition-patterns/rules/architecture-compound-components.md +112 -0
  88. package/catalogs/skills/vercel-composition-patterns/rules/patterns-children-over-render-props.md +87 -0
  89. package/catalogs/skills/vercel-composition-patterns/rules/patterns-explicit-variants.md +100 -0
  90. package/catalogs/skills/vercel-composition-patterns/rules/react19-no-forwardref.md +42 -0
  91. package/catalogs/skills/vercel-composition-patterns/rules/state-context-interface.md +191 -0
  92. package/catalogs/skills/vercel-composition-patterns/rules/state-decouple-implementation.md +113 -0
  93. package/catalogs/skills/vercel-composition-patterns/rules/state-lift-state.md +125 -0
  94. package/catalogs/skills/vercel-react-best-practices/AGENTS.md +3810 -0
  95. package/catalogs/skills/vercel-react-best-practices/LICENSE.txt +27 -0
  96. package/catalogs/skills/vercel-react-best-practices/README.md +123 -0
  97. package/catalogs/skills/vercel-react-best-practices/SKILL.md +149 -0
  98. package/catalogs/skills/vercel-react-best-practices/metadata.json +15 -0
  99. package/catalogs/skills/vercel-react-best-practices/rules/_sections.md +46 -0
  100. package/catalogs/skills/vercel-react-best-practices/rules/_template.md +28 -0
  101. package/catalogs/skills/vercel-react-best-practices/rules/advanced-effect-event-deps.md +56 -0
  102. package/catalogs/skills/vercel-react-best-practices/rules/advanced-event-handler-refs.md +55 -0
  103. package/catalogs/skills/vercel-react-best-practices/rules/advanced-init-once.md +42 -0
  104. package/catalogs/skills/vercel-react-best-practices/rules/advanced-use-latest.md +39 -0
  105. package/catalogs/skills/vercel-react-best-practices/rules/async-api-routes.md +38 -0
  106. package/catalogs/skills/vercel-react-best-practices/rules/async-cheap-condition-before-await.md +37 -0
  107. package/catalogs/skills/vercel-react-best-practices/rules/async-defer-await.md +82 -0
  108. package/catalogs/skills/vercel-react-best-practices/rules/async-dependencies.md +51 -0
  109. package/catalogs/skills/vercel-react-best-practices/rules/async-parallel.md +28 -0
  110. package/catalogs/skills/vercel-react-best-practices/rules/async-suspense-boundaries.md +99 -0
  111. package/catalogs/skills/vercel-react-best-practices/rules/bundle-analyzable-paths.md +63 -0
  112. package/catalogs/skills/vercel-react-best-practices/rules/bundle-barrel-imports.md +60 -0
  113. package/catalogs/skills/vercel-react-best-practices/rules/bundle-conditional.md +31 -0
  114. package/catalogs/skills/vercel-react-best-practices/rules/bundle-defer-third-party.md +49 -0
  115. package/catalogs/skills/vercel-react-best-practices/rules/bundle-dynamic-imports.md +35 -0
  116. package/catalogs/skills/vercel-react-best-practices/rules/bundle-preload.md +50 -0
  117. package/catalogs/skills/vercel-react-best-practices/rules/client-event-listeners.md +74 -0
  118. package/catalogs/skills/vercel-react-best-practices/rules/client-localstorage-schema.md +71 -0
  119. package/catalogs/skills/vercel-react-best-practices/rules/client-passive-event-listeners.md +48 -0
  120. package/catalogs/skills/vercel-react-best-practices/rules/client-swr-dedup.md +56 -0
  121. package/catalogs/skills/vercel-react-best-practices/rules/js-batch-dom-css.md +107 -0
  122. package/catalogs/skills/vercel-react-best-practices/rules/js-cache-function-results.md +80 -0
  123. package/catalogs/skills/vercel-react-best-practices/rules/js-cache-property-access.md +28 -0
  124. package/catalogs/skills/vercel-react-best-practices/rules/js-cache-storage.md +70 -0
  125. package/catalogs/skills/vercel-react-best-practices/rules/js-combine-iterations.md +32 -0
  126. package/catalogs/skills/vercel-react-best-practices/rules/js-early-exit.md +50 -0
  127. package/catalogs/skills/vercel-react-best-practices/rules/js-flatmap-filter.md +60 -0
  128. package/catalogs/skills/vercel-react-best-practices/rules/js-hoist-regexp.md +45 -0
  129. package/catalogs/skills/vercel-react-best-practices/rules/js-index-maps.md +37 -0
  130. package/catalogs/skills/vercel-react-best-practices/rules/js-length-check-first.md +49 -0
  131. package/catalogs/skills/vercel-react-best-practices/rules/js-min-max-loop.md +82 -0
  132. package/catalogs/skills/vercel-react-best-practices/rules/js-request-idle-callback.md +105 -0
  133. package/catalogs/skills/vercel-react-best-practices/rules/js-set-map-lookups.md +24 -0
  134. package/catalogs/skills/vercel-react-best-practices/rules/js-tosorted-immutable.md +57 -0
  135. package/catalogs/skills/vercel-react-best-practices/rules/rendering-activity.md +26 -0
  136. package/catalogs/skills/vercel-react-best-practices/rules/rendering-animate-svg-wrapper.md +47 -0
  137. package/catalogs/skills/vercel-react-best-practices/rules/rendering-conditional-render.md +40 -0
  138. package/catalogs/skills/vercel-react-best-practices/rules/rendering-content-visibility.md +38 -0
  139. package/catalogs/skills/vercel-react-best-practices/rules/rendering-hoist-jsx.md +46 -0
  140. package/catalogs/skills/vercel-react-best-practices/rules/rendering-hydration-no-flicker.md +82 -0
  141. package/catalogs/skills/vercel-react-best-practices/rules/rendering-hydration-suppress-warning.md +30 -0
  142. package/catalogs/skills/vercel-react-best-practices/rules/rendering-resource-hints.md +85 -0
  143. package/catalogs/skills/vercel-react-best-practices/rules/rendering-script-defer-async.md +68 -0
  144. package/catalogs/skills/vercel-react-best-practices/rules/rendering-svg-precision.md +28 -0
  145. package/catalogs/skills/vercel-react-best-practices/rules/rendering-usetransition-loading.md +75 -0
  146. package/catalogs/skills/vercel-react-best-practices/rules/rerender-defer-reads.md +39 -0
  147. package/catalogs/skills/vercel-react-best-practices/rules/rerender-dependencies.md +45 -0
  148. package/catalogs/skills/vercel-react-best-practices/rules/rerender-derived-state-no-effect.md +40 -0
  149. package/catalogs/skills/vercel-react-best-practices/rules/rerender-derived-state.md +29 -0
  150. package/catalogs/skills/vercel-react-best-practices/rules/rerender-functional-setstate.md +74 -0
  151. package/catalogs/skills/vercel-react-best-practices/rules/rerender-lazy-state-init.md +58 -0
  152. package/catalogs/skills/vercel-react-best-practices/rules/rerender-memo-with-default-value.md +38 -0
  153. package/catalogs/skills/vercel-react-best-practices/rules/rerender-memo.md +44 -0
  154. package/catalogs/skills/vercel-react-best-practices/rules/rerender-move-effect-to-event.md +45 -0
  155. package/catalogs/skills/vercel-react-best-practices/rules/rerender-no-inline-components.md +82 -0
  156. package/catalogs/skills/vercel-react-best-practices/rules/rerender-simple-expression-in-memo.md +35 -0
  157. package/catalogs/skills/vercel-react-best-practices/rules/rerender-split-combined-hooks.md +64 -0
  158. package/catalogs/skills/vercel-react-best-practices/rules/rerender-transitions.md +40 -0
  159. package/catalogs/skills/vercel-react-best-practices/rules/rerender-use-deferred-value.md +59 -0
  160. package/catalogs/skills/vercel-react-best-practices/rules/rerender-use-ref-transient-values.md +73 -0
  161. package/catalogs/skills/vercel-react-best-practices/rules/server-after-nonblocking.md +73 -0
  162. package/catalogs/skills/vercel-react-best-practices/rules/server-auth-actions.md +96 -0
  163. package/catalogs/skills/vercel-react-best-practices/rules/server-cache-lru.md +41 -0
  164. package/catalogs/skills/vercel-react-best-practices/rules/server-cache-react.md +76 -0
  165. package/catalogs/skills/vercel-react-best-practices/rules/server-dedup-props.md +65 -0
  166. package/catalogs/skills/vercel-react-best-practices/rules/server-hoist-static-io.md +149 -0
  167. package/catalogs/skills/vercel-react-best-practices/rules/server-no-shared-module-state.md +50 -0
  168. package/catalogs/skills/vercel-react-best-practices/rules/server-parallel-fetching.md +83 -0
  169. package/catalogs/skills/vercel-react-best-practices/rules/server-parallel-nested-fetching.md +34 -0
  170. package/catalogs/skills/vercel-react-best-practices/rules/server-serialization.md +38 -0
  171. package/catalogs/skills/webapp-testing/LICENSE.txt +202 -0
  172. package/catalogs/skills/webapp-testing/SKILL.md +96 -0
  173. package/catalogs/skills/webapp-testing/examples/console_logging.py +35 -0
  174. package/catalogs/skills/webapp-testing/examples/element_discovery.py +40 -0
  175. package/catalogs/skills/webapp-testing/examples/static_html_automation.py +33 -0
  176. package/catalogs/skills/webapp-testing/scripts/with_server.py +106 -0
  177. package/catalogs/skills.json +1115 -0
  178. package/docs/skills/evidencias/2026-10-28-evaluacion-inicial-mcp-seguridad.md +118 -0
  179. package/docs/skills/evidencias/2026-10-28-incorporacion-mcp-seguridad.json +152 -0
  180. package/docs/skills/evidencias/2026-10-28-recomendador-preparacion.md +50 -0
  181. package/package.json +73 -4
  182. package/plugins/nexakit-modelo/.claude-plugin/plugin.json +1 -0
  183. package/plugins/nexakit-modelo/hooks/hooks.json +1 -0
  184. package/plugins/nexakit-modelo/hooks/register.js +48 -0
  185. package/plugins/nexakit-modelo/hooks/status.js +8 -0
  186. package/plugins/nexakit-modelo/tests/register.test.ts +21 -0
  187. package/plugins/nexakit-panel/.claude-plugin/plugin.json +1 -0
  188. package/plugins/nexakit-panel/README.md +20 -0
  189. package/plugins/nexakit-panel/hooks/hooks.json +1 -0
  190. package/plugins/nexakit-panel/hooks/lienzo.js +382 -0
  191. package/plugins/nexakit-panel/hooks/panel.js +279 -0
  192. package/plugins/nexakit-panel/hooks/register.tsx +248 -0
  193. package/plugins/nexakit-panel/tests/lienzo.test.ts +142 -0
  194. package/plugins/nexakit-panel/tests/panel.test.ts +240 -0
  195. package/plugins/nexakit-panel/tests/register.test.ts +167 -0
  196. package/plugins/nexakit-panel/types/index.d.ts +41 -0
  197. package/plugins/nexakit-sesion/.claude-plugin/plugin.json +1 -0
  198. package/plugins/nexakit-sesion/README.md +12 -0
  199. package/plugins/nexakit-sesion/hooks/aviso.js +38 -0
  200. package/plugins/nexakit-sesion/hooks/hooks.json +1 -0
  201. package/plugins/nexakit-sesion/hooks/register.js +28 -0
  202. package/plugins/nexakit-sesion/tests/register.test.ts +96 -0
  203. package/plugins/niveles/.claude-plugin/plugin.json +5 -0
  204. package/plugins/niveles/agents/nivel-alto.md +13 -0
  205. package/plugins/niveles/agents/nivel-exigente.md +13 -0
  206. package/plugins/niveles/agents/nivel-ligero.md +13 -0
  207. package/plugins/niveles/agents/nivel-normal.md +13 -0
  208. package/schemas/agent-client-adapter.schema.json +786 -0
  209. package/schemas/agent-client-auth-diagnostic.schema.json +801 -0
  210. package/schemas/agent-client-isolated-variant.schema.json +105 -0
  211. package/schemas/agent-client-isolated-verification.schema.json +99 -0
  212. package/schemas/agent-client-managed-preferences-invocation.schema.json +67 -0
  213. package/schemas/agent-client-managed-preferences.schema.json +63 -0
  214. package/schemas/agent-client-native-diagnostics.schema.json +85 -0
  215. package/schemas/agent-client-native-evaluation.schema.json +171 -0
  216. package/schemas/agent-client-native-preflight.schema.json +90 -0
  217. package/schemas/agent-client-output.schema.json +551 -0
  218. package/schemas/agent-client-runtime-fixture.schema.json +90 -0
  219. package/schemas/agent-client-runtime-local.schema.json +95 -0
  220. package/schemas/agent-client-runtime.schema.json +1288 -0
  221. package/schemas/agent-client-selective-config.schema.json +77 -0
  222. package/schemas/agent-client-supervised-local.schema.json +135 -0
  223. package/schemas/agent-client-supervised.schema.json +186 -0
  224. package/schemas/agent-model-preference.schema.json +573 -0
  225. package/schemas/agent-model-selection.schema.json +311 -0
  226. package/schemas/agent-task-approvals.schema.json +362 -0
  227. package/schemas/agent-task-prepare.schema.json +1324 -0
  228. package/schemas/agent-task-verify.schema.json +198 -0
  229. package/schemas/project-memory-update.schema.json +33 -0
  230. package/schemas/project-memory.schema.json +298 -0
  231. package/schemas/skill-catalog.schema.json +246 -0
  232. package/schemas/task-workflow-plan-output.schema.json +35 -0
  233. package/scripts/prepare-package.js +6 -0
  234. package/scripts/validate-skill-catalog.js +41 -0
  235. package/skills/cerrar-avisens/SKILL.md +12 -0
  236. package/skills/cerrar-memoria/SKILL.md +96 -0
  237. package/skills/cerrar-memoria/references/cierre.md +76 -0
  238. package/skills/decision-records/SKILL.md +27 -0
  239. package/skills/decision-records/agents/openai.yaml +7 -0
  240. package/skills/decision-records/references/adr-template.md +35 -0
  241. package/skills/differential-review/LICENSE.txt +427 -0
  242. package/skills/differential-review/SKILL.md +225 -0
  243. package/skills/differential-review/adversarial.md +203 -0
  244. package/skills/differential-review/agents/openai.yaml +6 -0
  245. package/skills/differential-review/assets/trail-of-bits-mark.svg +1 -0
  246. package/skills/differential-review/methodology.md +234 -0
  247. package/skills/differential-review/patterns.md +300 -0
  248. package/skills/differential-review/reporting.md +369 -0
  249. package/skills/elegir-modelo/SKILL.md +68 -0
  250. package/skills/elegir-modelo/modelos.mjs +108 -0
  251. package/skills/frontend-design/LICENSE.txt +177 -0
  252. package/skills/frontend-design/SKILL.md +71 -0
  253. package/skills/gemini/SKILL.md +52 -0
  254. package/skills/imagen/SKILL.md +12 -0
  255. package/skills/investigar/SKILL.md +11 -0
  256. package/skills/mcp-builder/LICENSE.txt +202 -0
  257. package/skills/mcp-builder/SKILL.md +236 -0
  258. package/skills/mcp-builder/reference/evaluation.md +602 -0
  259. package/skills/mcp-builder/reference/mcp_best_practices.md +249 -0
  260. package/skills/mcp-builder/reference/node_mcp_server.md +970 -0
  261. package/skills/mcp-builder/reference/python_mcp_server.md +719 -0
  262. package/skills/mcp-builder/scripts/connections.py +151 -0
  263. package/skills/mcp-builder/scripts/evaluation.py +373 -0
  264. package/skills/mcp-builder/scripts/example_evaluation.xml +22 -0
  265. package/skills/mcp-builder/scripts/requirements.txt +2 -0
  266. package/skills/ordenar-proyecto/SKILL.md +56 -0
  267. package/skills/project-handoff/SKILL.md +53 -0
  268. package/skills/project-handoff/agents/openai.yaml +7 -0
  269. package/skills/project-handoff/references/handoff-template.md +35 -0
  270. package/skills/quality-gate/LICENSE.txt +26 -0
  271. package/skills/quality-gate/SKILL.md +44 -0
  272. package/skills/quality-gate/agents/openai.yaml +7 -0
  273. package/skills/quality-gate/references/review-checklist.md +42 -0
  274. package/skills/revisar/SKILL.md +11 -0
  275. package/skills/revisar-skill/LICENSE-find-skills.txt +26 -0
  276. package/skills/revisar-skill/SKILL.md +94 -0
  277. package/skills/revision-cruzada/SKILL.md +59 -0
  278. package/skills/web-design-guidelines/AGENTS.md +156 -0
  279. package/skills/web-design-guidelines/LICENSE.txt +21 -0
  280. package/skills/web-design-guidelines/SKILL.md +29 -0
  281. package/src/agent-models.js +131 -0
  282. package/src/brand.js +117 -0
  283. package/src/claude-code-runner.js +642 -0
  284. package/src/cli-executable.js +88 -0
  285. package/src/cli.js +778 -0
  286. package/src/client-adapter-plan.js +271 -0
  287. package/src/client-auth-diagnostic-parser.js +211 -0
  288. package/src/client-auth-diagnostic.js +257 -0
  289. package/src/client-isolated-variant.js +106 -0
  290. package/src/client-isolated-verification.js +130 -0
  291. package/src/client-local-platform.js +14 -0
  292. package/src/client-managed-preferences-invocation.js +209 -0
  293. package/src/client-managed-preferences.js +78 -0
  294. package/src/client-native-access.js +134 -0
  295. package/src/client-native-diagnostics.js +253 -0
  296. package/src/client-native-evaluation.js +106 -0
  297. package/src/client-native-preflight-files.js +68 -0
  298. package/src/client-native-preflight.js +95 -0
  299. package/src/client-output-json.js +118 -0
  300. package/src/client-output-parser.js +278 -0
  301. package/src/client-runtime-admission.js +80 -0
  302. package/src/client-runtime-coordinator.js +70 -0
  303. package/src/client-runtime-core.js +92 -0
  304. package/src/client-runtime-fixture-permit.js +20 -0
  305. package/src/client-runtime-fixture-protocol.js +39 -0
  306. package/src/client-runtime-state.js +67 -0
  307. package/src/client-runtime-store-files.js +91 -0
  308. package/src/client-runtime-store.js +239 -0
  309. package/src/client-runtime-supervisor.js +146 -0
  310. package/src/client-selective-config.js +195 -0
  311. package/src/client-selective-files.js +126 -0
  312. package/src/client-supervised-codes.js +32 -0
  313. package/src/client-supervised-contract.js +52 -0
  314. package/src/client-supervised-coordinator.js +36 -0
  315. package/src/client-supervised-permit.js +20 -0
  316. package/src/client-supervised-protocol.js +37 -0
  317. package/src/client-supervised-store.js +167 -0
  318. package/src/client-supervised-supervisor.js +167 -0
  319. package/src/client-supervised.js +160 -0
  320. package/src/codex-cli-runner.js +358 -0
  321. package/src/codex-delivery.js +101 -0
  322. package/src/connect.js +703 -0
  323. package/src/doctor.js +795 -0
  324. package/src/gemini-cli.js +76 -0
  325. package/src/gemini-files.js +45 -0
  326. package/src/gemini-tools.js +249 -0
  327. package/src/init.js +473 -0
  328. package/src/install-lock.js +127 -0
  329. package/src/install-registry.js +61 -0
  330. package/src/legacy-repair.js +71 -0
  331. package/src/level-agents.js +111 -0
  332. package/src/link-states.js +172 -0
  333. package/src/memory-connection-artifacts.js +192 -0
  334. package/src/memory-connection-descriptor.js +158 -0
  335. package/src/memory-connection-diagnostic.js +177 -0
  336. package/src/memory-connection-files.js +191 -0
  337. package/src/memory-connection-manifest.js +118 -0
  338. package/src/memory-connection-package.js +176 -0
  339. package/src/memory-connection-plan.js +211 -0
  340. package/src/memory-connection-skill.js +211 -0
  341. package/src/memory-connection-toml.js +153 -0
  342. package/src/memory-locator.js +118 -0
  343. package/src/memory-mcp-policy.js +181 -0
  344. package/src/memory-mcp-server.js +166 -0
  345. package/src/memory-mcp-transport.js +59 -0
  346. package/src/missing-dependencies.js +21 -0
  347. package/src/model-command.js +147 -0
  348. package/src/model-preference.js +235 -0
  349. package/src/model-selection.js +368 -0
  350. package/src/native/managed-preferences.c +100 -0
  351. package/src/native/managed-preferences.h +17 -0
  352. package/src/package-lock-path.js +13 -0
  353. package/src/panel-cli.js +182 -0
  354. package/src/peer-handoff.js +27 -0
  355. package/src/peer-inbox-cli.js +211 -0
  356. package/src/peer-review-mcp.js +210 -0
  357. package/src/peer-review-store.js +532 -0
  358. package/src/platform.js +24 -0
  359. package/src/project-root.js +41 -0
  360. package/src/project-signals.js +339 -0
  361. package/src/recommend-report.js +77 -0
  362. package/src/review-instructions.js +211 -0
  363. package/src/review-policy.js +35 -0
  364. package/src/revision-adapters.js +157 -0
  365. package/src/revision-context.js +86 -0
  366. package/src/revision-isolation.js +222 -0
  367. package/src/revision-process.js +103 -0
  368. package/src/revision-provenance.js +89 -0
  369. package/src/revision-snapshot.js +120 -0
  370. package/src/revision.js +242 -0
  371. package/src/runtime-fixtures/runtime-supervisor/child.cjs +3 -0
  372. package/src/runtime-fixtures/runtime-supervisor/fixture.cjs +49 -0
  373. package/src/runtime-fixtures/supervised/child.cjs +3 -0
  374. package/src/runtime-fixtures/supervised/fixture.cjs +47 -0
  375. package/src/sensitive-data.js +21 -0
  376. package/src/setup-apply.js +231 -0
  377. package/src/setup-plan.js +231 -0
  378. package/src/setup-state.js +277 -0
  379. package/src/setup-tui.js +555 -0
  380. package/src/skill-catalog-add.js +91 -0
  381. package/src/skill-catalog-audit.js +68 -0
  382. package/src/skill-catalog.js +170 -0
  383. package/src/skill-files.js +39 -0
  384. package/src/skill-recommendations.js +1673 -0
  385. package/src/skill-scan-report.js +14 -0
  386. package/src/skill-scan.js +178 -0
  387. package/src/stable-command.js +53 -0
  388. package/src/stable-install.js +222 -0
  389. package/src/status.js +200 -0
  390. package/src/superpowers.js +133 -0
  391. package/src/sync-apply.js +227 -0
  392. package/src/sync-files.js +109 -0
  393. package/src/sync-inspect.js +394 -0
  394. package/src/sync-ledger.js +94 -0
  395. package/src/sync-markdown.js +151 -0
  396. package/src/sync-proposal.js +84 -0
  397. package/src/sync-record.js +459 -0
  398. package/src/sync.js +360 -0
  399. package/src/task-approvals.js +367 -0
  400. package/src/task-cli.js +79 -0
  401. package/src/task-external-verifier.js +584 -0
  402. package/src/task-prepare-files.js +115 -0
  403. package/src/task-prepare.js +180 -0
  404. package/src/task-verify-cli.js +67 -0
  405. package/src/task-verify-files.js +188 -0
  406. package/src/task-verify.js +118 -0
  407. package/src/task-workflow-mcp.js +544 -0
  408. package/src/task-workflow-store.js +254 -0
  409. package/src/terminal-text.js +68 -0
  410. package/src/toolkit-cli.js +543 -0
  411. package/src/toolkit-commands-guide.js +107 -0
  412. package/src/toolkit-setup.js +614 -0
  413. package/src/usage-metrics.js +43 -0
  414. package/templates/agent-team/01-encargo.md +158 -0
  415. package/templates/agent-team/02-propuesta.md +132 -0
  416. package/templates/agent-team/03-revision.md +150 -0
  417. package/templates/agent-team/README.md +213 -0
  418. package/templates/model-inventory/01-inventario.md +154 -0
  419. package/templates/model-inventory/README.md +115 -0
  420. package/templates/project-memory/README.md +67 -0
  421. package/templates/project-memory/project-memory.example.json +53 -0
  422. package/templates/project-memory/skill/project-id/SKILL.md +37 -0
  423. package/templates/project-memory/skill/project-id/references/project-profile.md +44 -0
  424. package/templates/project-memory/vault/00-INICIO/Estado actual.md +44 -0
  425. package/templates/project-memory/vault/00-INICIO/Fuentes de verdad.md +30 -0
  426. package/templates/project-memory/vault/00-INICIO/Inicio del proyecto.md +44 -0
  427. package/templates/project-memory/vault/00-INICIO/Mantenimiento de la memoria.md +26 -0
  428. package/templates/project-memory/vault/00-INICIO/Mapa de la memoria.md +33 -0
  429. package/templates/project-memory/vault/00-INICIO/Registro de sincronizaci/303/263n.md" +28 -0
  430. package/templates/project-memory/vault/01-PRODUCTO/Producto.md +16 -0
  431. package/templates/project-memory/vault/02-ARQUITECTURA/Arquitectura.md +16 -0
  432. package/templates/project-memory/vault/03-IMPLEMENTACION/Implementaci/303/263n.md +15 -0
  433. package/templates/project-memory/vault/04-DECISIONES/Decisiones.md +19 -0
  434. package/templates/project-memory/vault/05-ENTREGAS/Entregas.md +12 -0
  435. package/templates/project-memory/vault/06-OPERACION/Operaci/303/263n.md +15 -0
  436. package/templates/project-memory/vault/07-AGENTES/Protocolo de agentes.md +33 -0
  437. package/templates/project-memory/vault/08-RECURSOS/Recursos.md +12 -0
  438. package/templates/project-memory/vault/09-ARCHIVO/README.md +5 -0
@@ -0,0 +1,67 @@
1
+ import { createClientRuntimeAdmission } from "./client-runtime-admission.js";
2
+ import { validators, copyInertJson, ParseFailure, fail, hash, same, fresh, ledgerFacts, recalculate } from "./client-runtime-core.js";
3
+
4
+ /** Calculate CAS proposals only. No mutable store, locks, clock or processes. */
5
+ export function createClientRuntimeState(base, preference, adapter, runtime) {
6
+ const validate = validators(base, preference, adapter, runtime);
7
+ const admit = createClientRuntimeAdmission(base, preference, adapter, runtime);
8
+ return function transition(input) {
9
+ const r = { schemaVersion: "1.0.0", engineVersion: "1.0.0", state: "invalid", executed: false, runtimeReady: false,
10
+ verifiedAdmission: false, persisted: false, operation: null, nextLedger: null, compareAndSwap: null, reasons: [] };
11
+ function finish(state, code) {
12
+ r.state = state; r.reasons = [code];
13
+ if (state !== "proposed") { r.nextLedger = null; r.compareAndSwap = null; }
14
+ if (!validate.stateReport(r)) throw Error("Runtime state invariant failed"); return r;
15
+ }
16
+ try {
17
+ const d = copyInertJson(input); if (!validate.stateInput(d)) fail("invalid-runtime-transition");
18
+ r.operation = d.operation; const beforeHash = hash(d.ledger);
19
+ if (d.expectedRevision !== d.ledger.revision || d.expectedLedgerSha256 !== beforeHash) return finish("blocked", "runtime-snapshot-conflict");
20
+ ledgerFacts(d.ledger, d.now);
21
+ if (d.ledger.revision >= 1_000_000) return finish("blocked", "runtime-revision-limit");
22
+ const next = copyInertJson(d.ledger); let a = next.attempts.find(a => a.attemptId === d.attemptId);
23
+ if (d.operation === "reserve") {
24
+ if (a) return finish("blocked", "runtime-attempt-already-exists");
25
+ if (!same(d.admissionInput.ledger, d.ledger) || d.admissionInput.now !== d.now) return finish("blocked", "runtime-admission-snapshot-conflict");
26
+ const admission = admit(d.admissionInput);
27
+ if (admission.state !== "eligible-for-reservation") return finish(admission.state === "invalid" ? "invalid" : "blocked", "runtime-reservation-not-eligible");
28
+ const v = admission.reservation;
29
+ a = { attemptId: d.attemptId, identity: v.identity, planSha256: v.planSha256, client: v.client,
30
+ profileId: v.profileId, model: v.model, effort: v.effort, state: "reserved", startObservation: "not-reported",
31
+ reservedCalls: v.calls, reservedSeconds: v.seconds, reservedAt: d.now, updatedAt: d.now,
32
+ actualCalls: null, actualSeconds: null, outcome: null, evidenceRef: null };
33
+ next.attempts.push(a);
34
+ } else {
35
+ if (!a) return finish("blocked", "runtime-attempt-not-found");
36
+ if (a.state === "settled") return finish("blocked", "runtime-attempt-already-settled");
37
+ if (Date.parse(d.now) < Date.parse(a.updatedAt)) return finish("blocked", "runtime-observation-before-attempt");
38
+ if (d.operation === "mark-started") {
39
+ if (a.state !== "reserved") return finish("blocked", "runtime-start-observation-conflict");
40
+ a.state = "started"; a.startObservation = "started";
41
+ } else if (d.operation === "interrupt") {
42
+ if (a.state === "reconciliation-required") return finish("blocked", "runtime-attempt-already-interrupted");
43
+ a.state = "reconciliation-required"; a.startObservation = a.startObservation === "started" ? "started" : "unknown"; a.outcome = "indeterminate";
44
+ } else {
45
+ if (d.reconciliation.status !== "approved" || d.reconciliation.revoked || !fresh(d.reconciliation, d.now, 3600) || !d.terminationConfirmed) return finish("blocked", "runtime-reconciliation-not-authorized-or-terminated");
46
+ if (d.reconciliation.projectId !== next.projectId || d.reconciliation.taskId !== next.taskId ||
47
+ d.reconciliation.attemptId !== a.attemptId || d.reconciliation.ledgerSha256 !== beforeHash) return finish("blocked", "runtime-reconciliation-binding-mismatch");
48
+ if (a.startObservation === "started" && d.startObservation !== "started") return finish("blocked", "runtime-start-observation-conflict");
49
+ if (d.startObservation === "not-started" && (d.actualCalls || d.actualSeconds || d.outcome !== "not-started")) return finish("blocked", "runtime-not-started-consumption-conflict");
50
+ if (d.startObservation === "started" && d.outcome === "not-started") return finish("blocked", "runtime-started-outcome-conflict");
51
+ Object.assign(a, { state: "settled", startObservation: d.startObservation, actualCalls: d.actualCalls,
52
+ actualSeconds: d.actualSeconds, outcome: d.outcome, evidenceRef: d.reconciliation.evidenceRef });
53
+ }
54
+ a.updatedAt = d.now;
55
+ }
56
+ next.revision++; recalculate(next);
57
+ if (!validate.ledger(next)) return finish("blocked", "runtime-ledger-counter-or-capacity-limit");
58
+ ledgerFacts(next, d.now);
59
+ r.nextLedger = next; r.compareAndSwap = { expectedRevision: d.expectedRevision, nextRevision: next.revision,
60
+ expectedSha256: beforeHash, nextSha256: hash(next) };
61
+ return finish("proposed", next.status === "reconciliation-required" ? "declared-transition-reconciliation-required" : "declared-transition-only");
62
+ } catch (error) {
63
+ if (!(error instanceof ParseFailure)) throw error;
64
+ return finish("invalid", error.code);
65
+ }
66
+ };
67
+ }
@@ -0,0 +1,91 @@
1
+ import fs from "node:fs/promises";
2
+ import { constants } from "node:fs";
3
+ import path from "node:path";
4
+ import { createHash } from "node:crypto";
5
+ import { ParseFailure, fail } from "./client-output-json.js";
6
+
7
+ export const FILE_LIMIT = 1024 * 1024;
8
+ export const bytesHash = bytes => createHash("sha256").update(bytes).digest("hex");
9
+ const missing = e => e?.code === "ENOENT";
10
+ export { missing };
11
+
12
+ // Local cooperative writers only. These checks are not openat-style containment.
13
+ export function createStoreFiles(overrides = {}) {
14
+ const io = { ...fs, ...overrides };
15
+ const uid = process.getuid?.() ?? null;
16
+ // Directory link counts change when our own task directory is created.
17
+ // File link count is independently required to remain one by privateType.
18
+ const identity = s => ({ dev: s.dev, ino: s.ino, uid: s.uid, mode: s.mode });
19
+ const equal = (a, b) => JSON.stringify(identity(a)) === JSON.stringify(identity(b));
20
+ function privateType(s, directory) {
21
+ if (s.uid !== uid || (s.mode & 0o7777) !== (directory ? 0o700 : 0o600) ||
22
+ (directory ? !s.isDirectory() : !s.isFile() || s.nlink !== 1)) fail("store-unsafe-resource");
23
+ }
24
+ async function physical(full) {
25
+ let current = path.parse(full).root;
26
+ for (const part of full.slice(current.length).split(path.sep).filter(Boolean)) {
27
+ current = path.join(current, part); const s = await io.lstat(current);
28
+ if (s.isSymbolicLink()) fail("store-symlink-path");
29
+ if (current !== full && !s.isDirectory()) fail("store-invalid-parent");
30
+ }
31
+ }
32
+ async function directory(full, expected) {
33
+ await physical(full); const s = await io.lstat(full); privateType(s, true);
34
+ if (expected && !equal(s, expected)) fail("store-directory-replaced");
35
+ return s;
36
+ }
37
+ async function syncDirectory(full, expected) {
38
+ await directory(full, expected);
39
+ const h = await io.open(full, constants.O_RDONLY | constants.O_DIRECTORY | constants.O_NOFOLLOW);
40
+ try { const s = await h.stat(); privateType(s, true); if (!equal(s, expected)) fail("store-directory-replaced"); await h.sync(); }
41
+ finally { await h.close(); }
42
+ await directory(full, expected);
43
+ }
44
+ async function read(full, limit = FILE_LIMIT) {
45
+ await physical(full); const before = await io.lstat(full); privateType(before, false);
46
+ if (before.size > limit) fail("store-file-limit");
47
+ const h = await io.open(full, constants.O_RDONLY | constants.O_NOFOLLOW);
48
+ try {
49
+ const opened = await h.stat(); privateType(opened, false);
50
+ if (!equal(before, opened) || opened.size > limit) fail("store-resource-changed");
51
+ const buffer = Buffer.alloc(limit + 1); let offset = 0;
52
+ while (offset < buffer.length) { const { bytesRead } = await h.read(buffer, offset, buffer.length - offset, offset); if (!bytesRead) break; offset += bytesRead; }
53
+ if (offset > limit) fail("store-file-limit");
54
+ const after = await h.stat(); const named = await io.lstat(full);
55
+ privateType(after, false); privateType(named, false);
56
+ if (!equal(opened, after) || !equal(after, named) || after.size !== offset || opened.size !== after.size || opened.mtimeMs !== after.mtimeMs) fail("store-resource-changed");
57
+ return { bytes: buffer.subarray(0, offset), stat: after };
58
+ } finally { await h.close(); }
59
+ }
60
+ async function exclusive(full) {
61
+ let h;
62
+ try { h = await io.open(full, constants.O_WRONLY | constants.O_CREAT | constants.O_EXCL | constants.O_NOFOLLOW, 0o600); }
63
+ catch (e) { if (e?.code === "EEXIST") fail("store-resource-exists"); throw e; }
64
+ try { const stat = await h.stat(); privateType(stat, false); return { handle: h, stat, full, digest: null }; }
65
+ catch { await h.close(); fail("store-created-resource-unusable"); }
66
+ }
67
+ async function write(resource, bytes) {
68
+ let offset = 0;
69
+ while (offset < bytes.length) {
70
+ const { bytesWritten } = await resource.handle.write(bytes, offset, bytes.length - offset, offset);
71
+ if (!bytesWritten) fail("store-short-write"); offset += bytesWritten;
72
+ }
73
+ resource.digest = bytesHash(bytes); await resource.handle.sync();
74
+ }
75
+ async function owned(resource) {
76
+ const r = await read(resource.full); if (!equal(r.stat, resource.stat)) fail("store-resource-replaced");
77
+ if (resource.digest !== null && bytesHash(r.bytes) !== resource.digest) fail("store-resource-replaced");
78
+ return r;
79
+ }
80
+ async function remove(resource, dir, expected) {
81
+ await directory(dir, expected); await owned(resource); await io.unlink(resource.full);
82
+ }
83
+ async function contents(dir, allowed) {
84
+ const h = await io.opendir(dir);
85
+ try { let n = 0; for (;;) { const e = await h.read(); if (!e) break; if (++n > allowed.length || !allowed.includes(e.name)) fail("store-residual-resource"); } }
86
+ finally { await h.close(); }
87
+ }
88
+ return { io, uid, equal, physical, directory, syncDirectory, read, exclusive, write, owned, remove, contents,
89
+ async exists(full) { try { return await io.lstat(full); } catch (e) { if (missing(e)) return null; throw e; } },
90
+ code(e) { return e instanceof ParseFailure ? e.code : "store-io-failed"; } };
91
+ }
@@ -0,0 +1,239 @@
1
+ import path from "node:path";
2
+ import { supportsLocalPosix } from "./client-local-platform.js";
3
+ import { fileURLToPath } from "node:url";
4
+ import { randomUUID } from "node:crypto";
5
+ import Ajv2020 from "ajv/dist/2020.js";
6
+ import { constants } from "node:fs";
7
+ import { createClientRuntimeState } from "./client-runtime-state.js";
8
+ import { copyInertJson, ParseFailure, fail, canonical, hash, same, fresh, ledgerFacts } from "./client-runtime-core.js";
9
+ import { decode, parseEventJson } from "./client-output-json.js";
10
+ import { createStoreFiles, bytesHash, FILE_LIMIT, missing } from "./client-runtime-store-files.js";
11
+ import { createFixturePermit, revokeFixturePermit } from "./client-runtime-fixture-permit.js";
12
+
13
+ export const LOCAL_STORE_ID = "https://agent-toolkit.local/schemas/agent-client-runtime-local.schema.json";
14
+ const toolkit = fileURLToPath(new URL("../", import.meta.url)).replace(/\/$/, "");
15
+ const contained = (child, parent) => child === parent || child.startsWith(parent + path.sep);
16
+ const validPath = p => path.isAbsolute(p) && path.normalize(p) === p && !p.includes("\0");
17
+ const utc = value => {
18
+ const t = Date.parse(value);
19
+ return typeof value === "string" && /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}Z$/.test(value) && Number.isFinite(t) && new Date(t).toISOString() === value.replace(/Z$/, ".000Z");
20
+ };
21
+
22
+ /** Trusted factory configuration/dependencies, inert JSON operations. No client launcher. */
23
+ export function createClientRuntimeStore(base, preference, adapter, runtime, local, configuration, dependencies = {}) {
24
+ const schemas = [base, preference, adapter, runtime, local].map(copyInertJson);
25
+ if (schemas[4].$id !== LOCAL_STORE_ID) throw Error("Unsupported local runtime schema");
26
+ const ajv = new Ajv2020({ strict: true, allErrors: true }); ajv.addFormat("date-time", { type: "string", validate: utc });
27
+ schemas.forEach(s => ajv.addSchema(s));
28
+ const check = Object.fromEntries(["configuration", "request", "document", "report"].map(n => [n, ajv.compile({ $ref: `${LOCAL_STORE_ID}#/$defs/${n}` })]));
29
+ const cfg = copyInertJson(configuration); if (!check.configuration(cfg)) throw Error("Invalid local runtime configuration");
30
+ const transition = createClientRuntimeState(...schemas.slice(0, 4));
31
+ const files = createStoreFiles(dependencies.io); const clock = dependencies.clock ?? Date.now;
32
+ const checkpoint = dependencies.checkpoint ?? (() => {});
33
+
34
+ async function execute(input, sessionConsumer, sessionGate) {
35
+ const r = { schemaVersion: "1.0.0", engineVersion: "1.0.0", scope: "cooperative-local-data-only", state: "blocked",
36
+ executed: false, nativeExecuted: false, runtimeReady: false, verifiedAdmission: false, writeObservation: "unchanged",
37
+ durability: "unconfirmed", cleanup: "complete", identity: null, snapshot: null, snapshotSha256: null, reasons: [] };
38
+ let root, rootStat, dir, dirStat, lock, temp, before = null, after = null, target, publishing = false, published = false;
39
+ let keep = false;
40
+ const sessionResources = [];
41
+ const phase = name => checkpoint(name);
42
+ function now() { const ms = clock(); if (typeof ms !== "number" || !Number.isFinite(ms)) fail("store-clock-unavailable"); const date = new Date(ms); if (!Number.isFinite(date.getTime())) fail("store-clock-unavailable"); return date.toISOString().replace(/\.\d{3}Z$/, "Z"); }
43
+ const document = ledger => ({ storageVersion: "1.0.0", identity: r.identity, ledgerSha256: hash(ledger), ledger });
44
+ function inspect(bytes, time) {
45
+ const d = copyInertJson(parseEventJson(decode(bytes)));
46
+ if (!check.document(d) || !same(d.identity, r.identity) || !same({ projectId: d.ledger.projectId, taskId: d.ledger.taskId }, r.identity) || d.ledgerSha256 !== hash(d.ledger)) fail("store-invalid-document");
47
+ ledgerFacts(d.ledger, time); return d;
48
+ }
49
+ async function roots() {
50
+ if (!validPath(cfg.root) || cfg.root === path.parse(cfg.root).root || !cfg.forbiddenRoots.every(validPath)) fail("store-invalid-root");
51
+ await files.physical(cfg.root); root = await files.io.realpath(cfg.root); rootStat = await files.directory(root);
52
+ const forbidden = [toolkit, ...cfg.forbiddenRoots];
53
+ for (const p of forbidden) {
54
+ let resolved = p; try { resolved = await files.io.realpath(p); } catch (e) { if (!missing(e)) throw e; }
55
+ if (contained(root, resolved) || contained(resolved, root)) fail("store-forbidden-root");
56
+ }
57
+ // Refuse roots within an existing repository or Obsidian vault without invoking Git.
58
+ for (let p = root; p !== path.parse(p).root; p = path.dirname(p)) {
59
+ if (await files.exists(path.join(p, ".git")) || await files.exists(path.join(p, ".obsidian"))) fail("store-forbidden-root");
60
+ }
61
+ }
62
+ function permission(d, time) {
63
+ const a = d.authorization;
64
+ if (a.status !== "approved" || a.revoked || !a.operations.includes(d.operation) || !fresh(a, time, 3600) ||
65
+ !same(a.identity, d.identity) || a.rootSha256 !== hash(root)) fail("store-not-authorized");
66
+ }
67
+ async function fence() {
68
+ await files.directory(root, rootStat); await files.directory(dir, dirStat); await files.owned(lock);
69
+ }
70
+ async function current(time) { const read = await files.read(target); return { ...read, data: inspect(read.bytes, time) }; }
71
+ async function unchanged(time) {
72
+ if (before === null) { if (await files.exists(target)) fail("store-snapshot-conflict"); }
73
+ else { const read = await current(time); if (!files.equal(before.stat, read.stat) || bytesHash(before.bytes) !== bytesHash(read.bytes)) fail("store-snapshot-conflict"); }
74
+ }
75
+ try {
76
+ const d = copyInertJson(input); if (!check.request(d)) fail("store-invalid-request"); r.identity = d.identity;
77
+ if (!supportsLocalPosix(process.platform) || cfg.evaluation.platform !== process.platform || cfg.evaluation.nodeVersion !== process.version ||
78
+ !constants.O_NOFOLLOW || !constants.O_DIRECTORY) fail("store-platform-not-evaluated");
79
+ let time = now(); await roots(); permission(d, time);
80
+ if (d.operation === "initialize") {
81
+ const imported = inspect(Buffer.from(canonical(document(d.ledger))), time);
82
+ const facts = ledgerFacts(imported.ledger, time);
83
+ if (d.importReferenceId !== imported.ledger.importEvidenceRef || imported.ledger.status !== "reconciled" || facts.active || facts.uncertain || facts.overrun) fail("store-import-not-reconciled");
84
+ } else if (d.operation === "apply" && !same({ projectId: d.transition.ledger.projectId, taskId: d.transition.ledger.taskId }, d.identity)) fail("store-transition-identity-mismatch");
85
+ dir = path.join(root, hash(d.identity)); target = path.join(dir, "ledger.json");
86
+ if (d.operation === "initialize") {
87
+ try { await files.io.mkdir(dir, { mode: 0o700 }); await files.syncDirectory(root, rootStat); }
88
+ catch (e) { if (e?.code !== "EEXIST") throw e; }
89
+ }
90
+ try { dirStat = await files.directory(dir); } catch (e) { if (missing(e)) fail("store-ledger-missing"); throw e; }
91
+ await files.contents(dir, ["ledger.json", "write.lock"]);
92
+ try { lock = await files.exclusive(path.join(dir, "write.lock")); }
93
+ catch (e) { if (e instanceof ParseFailure && e.code === "store-resource-exists") fail("store-locked"); throw e; }
94
+ await phase("lock-created"); await files.write(lock, Buffer.from(canonical({ protocolVersion: "1.0.0", ownerId: randomUUID() }) + "\n"));
95
+ await files.syncDirectory(dir, dirStat); await phase("lock-durable");
96
+ await fence(); time = now(); permission(d, time);
97
+ if (await files.exists(target)) before = await current(time);
98
+ await phase("snapshot-read");
99
+ if (d.operation === "initialize") {
100
+ if (before !== null) fail("store-already-initialized"); after = document(d.ledger);
101
+ } else {
102
+ if (before === null) fail("store-ledger-missing");
103
+ if (d.operation === "read") { r.state = "unchanged"; r.snapshot = before.data.ledger; r.snapshotSha256 = before.data.ledgerSha256; r.reasons = ["store-snapshot-read"]; }
104
+ else {
105
+ const t = copyInertJson(d.transition);
106
+ if (!same(t.ledger, before.data.ledger) || t.expectedRevision !== before.data.ledger.revision || t.expectedLedgerSha256 !== before.data.ledgerSha256) fail("store-snapshot-conflict");
107
+ t.now = time;
108
+ if (t.operation === "reserve") { t.admissionInput.now = time; t.admissionInput.ledger = copyInertJson(before.data.ledger); }
109
+ const proposal = transition(t); if (proposal.state !== "proposed") fail("store-transition-blocked");
110
+ after = document(proposal.nextLedger);
111
+ }
112
+ }
113
+ if (after) {
114
+ const bytes = Buffer.from(canonical(after) + "\n"); if (bytes.length > FILE_LIMIT) fail("store-file-limit");
115
+ inspect(bytes, time);
116
+ temp = await files.exclusive(path.join(dir, `.ledger-${randomUUID()}.tmp`)); await phase("temp-created");
117
+ await files.write(temp, bytes); await phase("temp-synced");
118
+ const readback = await files.owned(temp); if (!same(inspect(readback.bytes, time), after)) fail("store-invalid-temporary");
119
+ await phase("before-publish"); time = now(); permission(d, time);
120
+ // Recheck temporal admission at publication, without refreshing evidence stamps.
121
+ if (d.operation === "apply") {
122
+ const t = copyInertJson(d.transition); t.now = time;
123
+ if (t.operation === "reserve") t.admissionInput.now = time;
124
+ const again = transition(t); if (again.state !== "proposed") fail("store-transition-blocked");
125
+ if (!same(again.nextLedger, after.ledger)) {
126
+ // Time-sensitive proposals must be regenerated, not silently installed stale.
127
+ fail("store-publication-time-changed");
128
+ }
129
+ } else inspect(bytes, time);
130
+ await fence(); await files.owned(temp); await unchanged(time);
131
+ publishing = true; await files.io.rename(temp.full, target); published = true; r.writeObservation = "replaced";
132
+ await phase("published"); await files.syncDirectory(dir, dirStat); r.durability = "confirmed";
133
+ await phase("directory-synced"); await fence(); const final = await current(now());
134
+ if (!same(final.data, after) || bytesHash(final.bytes) !== temp.digest) fail("store-publication-mismatch");
135
+ await phase("confirmed"); r.state = "committed"; r.snapshot = final.data.ledger; r.snapshotSha256 = final.data.ledgerSha256; r.reasons = ["store-transition-committed"];
136
+ if (sessionConsumer) {
137
+ // Internal session: lock remains held. A public committed JSON cannot enter here.
138
+ let active = true;
139
+ const pending = new Set();
140
+ const attemptId = d.transition.attemptId;
141
+ const getSnapshot = () => copyInertJson(r.snapshot);
142
+ const guard = async () => {
143
+ if (!active) fail("store-session-closed");
144
+ const time = now(); permission(d, time); await sessionGate(time);
145
+ await fence(); const observed = await current(time);
146
+ if (!same(observed.data.ledger, r.snapshot)) fail("store-snapshot-conflict");
147
+ };
148
+ const performSession = async value => {
149
+ await guard();
150
+ const t = copyInertJson(value);
151
+ if (!["mark-started", "interrupt", "settle"].includes(t.operation) || t.attemptId !== attemptId ||
152
+ !check.request({ ...d, transition: t }) || !same(t.ledger, r.snapshot) ||
153
+ t.expectedRevision !== r.snapshot.revision || t.expectedLedgerSha256 !== hash(r.snapshot)) fail("store-session-transition-invalid");
154
+ let time = now(); t.now = time;
155
+ const proposed = transition(t); if (proposed.state !== "proposed") fail("store-transition-blocked");
156
+ const previous = await current(time); after = document(proposed.nextLedger);
157
+ const bytes = Buffer.from(canonical(after) + "\n"); if (bytes.length > FILE_LIMIT) fail("store-file-limit"); inspect(bytes, time);
158
+ const next = await files.exclusive(path.join(dir, `.ledger-${randomUUID()}.tmp`)); sessionResources.push(next);
159
+ await files.write(next, bytes); await phase(`session-${t.operation}-before-publish`);
160
+ const readback = await files.owned(next); if (!same(inspect(readback.bytes, time), after)) fail("store-invalid-temporary");
161
+ time = now(); t.now = time; await guard();
162
+ const checked = transition(t);
163
+ if (checked.state !== "proposed" || !same(checked.nextLedger, after.ledger)) fail("store-publication-time-changed");
164
+ const old = await current(time);
165
+ if (!files.equal(previous.stat, old.stat) || bytesHash(previous.bytes) !== bytesHash(old.bytes)) fail("store-snapshot-conflict");
166
+ await files.owned(next); r.durability = "unconfirmed";
167
+ await files.io.rename(next.full, target); await phase(`session-${t.operation}-published`);
168
+ await files.syncDirectory(dir, dirStat); r.durability = "confirmed"; await fence();
169
+ const confirmed = await current(now());
170
+ if (!same(confirmed.data, after) || bytesHash(confirmed.bytes) !== next.digest) fail("store-publication-mismatch");
171
+ r.snapshot = confirmed.data.ledger; r.snapshotSha256 = confirmed.data.ledgerSha256;
172
+ await phase(`session-${t.operation}-confirmed`);
173
+ return getSnapshot();
174
+ };
175
+ const applySession = value => {
176
+ const operation = performSession(value); pending.add(operation);
177
+ operation.then(() => pending.delete(operation), () => pending.delete(operation));
178
+ return operation;
179
+ };
180
+ const bindingSha256 = await sessionGate(now());
181
+ const handle = createFixturePermit(async () => {
182
+ await guard();
183
+ if (r.snapshot.attempts.find(a => a.attemptId === attemptId)?.state !== "reserved") fail("store-session-not-reserved");
184
+ }, () => applySession({ schemaVersion: "1.0.0", operation: "mark-started", now: now(), ledger: getSnapshot(),
185
+ expectedRevision: r.snapshot.revision, expectedLedgerSha256: r.snapshotSha256, attemptId }), bindingSha256);
186
+ try {
187
+ await phase("session-reserved");
188
+ await sessionConsumer(Object.freeze({ handle, snapshot: getSnapshot, apply: applySession }));
189
+ if (r.snapshot.attempts.find(a => a.attemptId === attemptId)?.state !== "settled") fail("store-session-reconciliation-required");
190
+ await guard();
191
+ } finally {
192
+ active = false; revokeFixturePermit(handle);
193
+ // Do not close descriptors or return while a started-observation write is still in flight.
194
+ await Promise.allSettled([...pending]);
195
+ }
196
+ }
197
+ }
198
+ } catch (e) {
199
+ const primary = files.code(e); r.reasons = [primary]; r.state = "blocked"; r.snapshot = null; r.snapshotSha256 = null;
200
+ if (primary === "store-created-resource-unusable") { r.state = "indeterminate"; r.cleanup = "intervention-required"; }
201
+ if (publishing) {
202
+ keep = true; r.state = "indeterminate";
203
+ try {
204
+ const observed = await current(now());
205
+ if (after && same(observed.data, after)) { published = true; r.writeObservation = "replaced"; }
206
+ else if (before && bytesHash(observed.bytes) === bytesHash(before.bytes)) r.writeObservation = "unchanged";
207
+ else r.writeObservation = "unknown";
208
+ } catch { r.writeObservation = "unknown"; }
209
+ }
210
+ } finally {
211
+ try {
212
+ if (lock) {
213
+ await phase("before-cleanup");
214
+ await files.directory(root, rootStat); await files.directory(dir, dirStat);
215
+ if (!keep) {
216
+ if (temp && !published) await files.remove(temp, dir, dirStat);
217
+ await files.remove(lock, dir, dirStat); await files.syncDirectory(dir, dirStat);
218
+ await phase("after-cleanup");
219
+ } else r.cleanup = "intervention-required";
220
+ }
221
+ } catch { r.cleanup = "intervention-required"; r.state = "indeterminate"; r.reasons.push("store-cleanup-required"); r.snapshot = null; r.snapshotSha256 = null; }
222
+ for (const resource of [...sessionResources, temp, lock]) if (resource) {
223
+ try { await resource.handle.close(); } catch { r.cleanup = "intervention-required"; r.state = "indeterminate"; r.reasons.push("store-close-unconfirmed"); }
224
+ }
225
+ }
226
+ if (r.state === "indeterminate" || r.state === "blocked") { r.snapshot = null; r.snapshotSha256 = null; }
227
+ r.reasons = [...new Set(r.reasons)];
228
+ if (!check.report(r)) throw Error("Local runtime store invariant failed");
229
+ return r;
230
+ }
231
+ const store = input => execute(input, null, null);
232
+ // Trusted C2.2 coordinator only. JSON operation API and all C2.1 reports stay unchanged.
233
+ store.withFixtureSession = async (input, consumer, gate) => {
234
+ const d = copyInertJson(input);
235
+ if (d?.operation !== "apply" || d?.transition?.operation !== "reserve" || typeof consumer !== "function" || typeof gate !== "function") throw Error("Invalid trusted fixture session");
236
+ return execute(d, consumer, gate);
237
+ };
238
+ return store;
239
+ }
@@ -0,0 +1,146 @@
1
+ import { supportsLocalPosix } from "./client-local-platform.js";
2
+ import fs from "node:fs/promises";
3
+ import os from "node:os";
4
+ import path from "node:path";
5
+ import { fileURLToPath } from "node:url";
6
+ import { spawn as nativeSpawn } from "node:child_process";
7
+ import { claimFixturePermit } from "./client-runtime-fixture-permit.js";
8
+ import { createFixtureProtocol } from "./client-runtime-fixture-protocol.js";
9
+ import { copyBytes, ParseFailure } from "./client-output-json.js";
10
+ import { copyInertJson, hash } from "./client-runtime-core.js";
11
+
12
+ export const FIXTURES = Object.freeze(["success", "split-utf8", "silent", "blocked-stdin", "failure", "stdout-overflow", "stderr-overflow", "long-line", "event-overflow", "invalid-utf8", "forbidden-event", "duplicate-json", "after-terminal", "unterminated", "incomplete", "ignore-term", "parent-child", "child-after-parent", "stderr-invalid", "environment", "closed-stdin"]);
13
+ const script = fileURLToPath(new URL("./runtime-fixtures/runtime-supervisor/fixture.cjs", import.meta.url));
14
+ export const monotonicMs = () => Number(process.hrtime.bigint() / 1_000_000n);
15
+ const reason = e => e instanceof ParseFailure ? e.code : "fixture-local-error";
16
+
17
+ /** Trusted fixtures only. Internal dependencies cannot arrive in request JSON. */
18
+ export async function superviseFixture(handle, request, { signal, beganAt, checkpoint = () => {}, spawn = nativeSpawn,
19
+ kill = process.kill.bind(process), monotonic = monotonicMs } = {}) {
20
+ const r = { scope: "fixture-only", state: "not-started", fixtureExecuted: false, spawnAttempts: 0,
21
+ nativeExecuted: false, runtimeReady: false, verifiedAdmission: false, modelCalls: 0,
22
+ started: false, exitObserved: false, pipesClosed: false, groupObservation: "not-started",
23
+ terminationConfirmed: false, stdoutBytes: 0, stderrBytes: 0, events: 0, elapsedMs: 0, reasons: [] };
24
+ const start = beganAt ?? monotonic(); let l;
25
+ let cwd, owned, capability, spawnReturned = false;
26
+ try {
27
+ request = copyInertJson(request); l = request.limits;
28
+ if (!supportsLocalPosix(process.platform) || !FIXTURES.includes(request.fixtureId)) throw Error("fixture-not-admitted");
29
+ const input = copyBytes(request.inputText, l.maxInputBytes);
30
+ if (signal?.aborted) { r.reasons = ["fixture-canceled-before-start"]; r.terminationConfirmed = true; return r; }
31
+ cwd = await fs.realpath(await fs.mkdtemp(path.join(os.tmpdir(), "agent-fixture-work-")));
32
+ await fs.chmod(cwd, 0o700); owned = await fs.lstat(cwd);
33
+ // Fixed packaged program and Node executable; never a path or recipe from JSON.
34
+ const source = await fs.lstat(script);
35
+ if (!source.isFile() || source.isSymbolicLink() || (source.mode & 0o022)) throw Error("fixture-source-unsafe");
36
+ if (monotonic() - start >= l.timeoutMs - l.graceMs - l.confirmationMs) throw Error("fixture-deadline-before-start");
37
+ await checkpoint("fixture-before-claim"); capability = await claimFixturePermit(handle, hash(request));
38
+ if (signal?.aborted || monotonic() - start >= l.timeoutMs - l.graceMs - l.confirmationMs) throw Error("fixture-stopped-before-start");
39
+ r.spawnAttempts = 1;
40
+ const child = spawn(process.execPath, [script, request.fixtureId], {
41
+ shell: false, detached: true, cwd, env: { LANG: "C", LC_ALL: "C" }, stdio: ["pipe", "pipe", "pipe"]
42
+ });
43
+ spawnReturned = true;
44
+ // No awaits between spawn and registration of all lifecycle/error handlers.
45
+ await new Promise(resolve => {
46
+ const protocol = createFixtureProtocol(l); const stderrDecoder = new TextDecoder("utf-8", { fatal: true });
47
+ let done = false, canceled = false, cancelAt = null, forced = false, terminalFailure = false;
48
+ let groupGone = false, spawnFailed = false, spawned = false, recordPending = false, recordFailed = false;
49
+ let stdinClosed = false, stdoutClosed = false, stderrClosed = false, closeObserved = false, exitCode = null;
50
+ let timer, hardTimer;
51
+ const add = code => { if (!r.reasons.includes(code)) r.reasons.push(code); };
52
+ const elapsed = () => Math.max(0, monotonic() - start);
53
+ const observeGroup = () => {
54
+ if (groupGone || spawnFailed) return false;
55
+ if (!spawned) return true;
56
+ try { kill(-child.pid, 0); return true; }
57
+ catch (e) { if (e?.code === "ESRCH") { groupGone = true; return false; } add("fixture-group-unconfirmed"); return true; }
58
+ };
59
+ const send = sig => {
60
+ if (!spawned || groupGone) return;
61
+ try { kill(-child.pid, sig); }
62
+ catch (e) { if (e?.code === "ESRCH") groupGone = true; else add("fixture-signal-unconfirmed"); }
63
+ };
64
+ const cancel = (code, state = "failed") => {
65
+ if (done) return;
66
+ add(code);
67
+ if (done || canceled) return;
68
+ canceled = true; terminalFailure = true; cancelAt = monotonic(); r.state = state;
69
+ child.stdin.destroy(); send("SIGTERM");
70
+ };
71
+ const finalize = uncertain => {
72
+ if (done) return; done = true; clearInterval(timer); clearTimeout(hardTimer); signal?.removeEventListener("abort", abort);
73
+ const counts = protocol.counts(); Object.assign(r, { stdoutBytes: counts.stdoutBytes, events: counts.events,
74
+ pipesClosed: stdinClosed && stdoutClosed && stderrClosed && closeObserved, elapsedMs: elapsed() });
75
+ r.groupObservation = spawnFailed ? "not-started" : groupGone ? "absent" : "unconfirmed";
76
+ // Process termination and uncertainty of storage/elapsed time are separate observations.
77
+ r.terminationConfirmed = spawnFailed || r.exitObserved && r.pipesClosed && groupGone;
78
+ if (uncertain || recordFailed || r.elapsedMs > l.timeoutMs) { r.state = "indeterminate"; add("fixture-reconciliation-required"); }
79
+ else if (spawnFailed) r.state = "not-started";
80
+ else if (!terminalFailure) r.state = "completed";
81
+ if (!r.reasons.length) add("fixture-completed");
82
+ // Never retain or print raw child data. Stop reads if closure is uncertain.
83
+ child.stdin.destroy(); child.stdout.destroy(); child.stderr.destroy(); resolve();
84
+ };
85
+ const poll = () => {
86
+ if (done) return;
87
+ const ms = elapsed();
88
+ if (ms >= l.timeoutMs - l.graceMs - l.confirmationMs && !canceled) cancel("fixture-deadline", "timed-out");
89
+ if (canceled && !forced && monotonic() - cancelAt >= l.graceMs) { forced = true; send("SIGKILL"); }
90
+ const alive = observeGroup();
91
+ if (r.exitObserved && closeObserved && stdinClosed && stdoutClosed && stderrClosed && !recordPending && !alive) {
92
+ if (!canceled && !spawnFailed) {
93
+ try { if (exitCode !== 0) throw Error("failed"); protocol.finish(); stderrDecoder.decode(); }
94
+ catch (e) { terminalFailure = true; r.state = "failed"; add(e instanceof ParseFailure ? e.code : "fixture-exit-or-stderr-invalid"); }
95
+ }
96
+ finalize(false);
97
+ } else if (r.exitObserved && alive && !canceled) cancel("fixture-descendants-live");
98
+ if (spawnFailed && closeObserved) finalize(false);
99
+ };
100
+ const abort = () => cancel("fixture-canceled", "canceled");
101
+ child.on("spawn", () => {
102
+ if (done) { spawned = true; send("SIGKILL"); return; }
103
+ spawned = true; r.started = true; r.fixtureExecuted = true; recordPending = true;
104
+ if (canceled) send("SIGTERM");
105
+ Promise.resolve().then(() => checkpoint("fixture-spawned")).then(() => capability.started())
106
+ .catch(() => { recordFailed = true; cancel("fixture-mark-started-unconfirmed"); })
107
+ .finally(() => { recordPending = false; poll(); });
108
+ });
109
+ child.on("error", () => { if (done) return; if (!spawned) { spawnFailed = true; add("fixture-spawn-failed"); } else cancel("fixture-process-error"); });
110
+ child.on("exit", code => { if (done) return; r.exitObserved = true; exitCode = code; poll(); });
111
+ child.on("close", () => { closeObserved = true; poll(); });
112
+ child.stdin.on("close", () => { stdinClosed = true; poll(); });
113
+ child.stdout.on("close", () => { stdoutClosed = true; poll(); });
114
+ child.stderr.on("close", () => { stderrClosed = true; poll(); });
115
+ child.stdin.on("error", () => cancel("fixture-stdin-error"));
116
+ child.stdout.on("error", () => cancel("fixture-stdout-error"));
117
+ child.stderr.on("error", () => cancel("fixture-stderr-error"));
118
+ child.stdout.on("data", chunk => {
119
+ if (done || canceled) return;
120
+ try { protocol.push(chunk); } catch (e) { cancel(reason(e)); }
121
+ });
122
+ child.stderr.on("data", chunk => {
123
+ if (done || canceled) return;
124
+ r.stderrBytes += chunk.length;
125
+ if (r.stderrBytes > l.maxStderrBytes) cancel("fixture-stderr-limit");
126
+ else try { stderrDecoder.decode(chunk, { stream: true }); } catch { cancel("fixture-stderr-invalid-utf8"); }
127
+ });
128
+ signal?.addEventListener("abort", abort, { once: true }); if (signal?.aborted) abort();
129
+ timer = setInterval(poll, 10);
130
+ hardTimer = setTimeout(() => { send("SIGKILL"); poll(); if (!done) finalize(true); }, Math.max(1, l.timeoutMs - elapsed()));
131
+ try { child.stdin.end(input, error => { if (error) cancel("fixture-stdin-error"); }); }
132
+ catch { cancel("fixture-stdin-error"); }
133
+ });
134
+ } catch (e) {
135
+ r.reasons = [e instanceof ParseFailure ? e.code : "fixture-start-blocked"];
136
+ r.terminationConfirmed = !spawnReturned; r.elapsedMs = Math.max(0, monotonic() - start);
137
+ // A synchronous spawn failure is known not-started; an unexpected post-spawn error is uncertain.
138
+ if (spawnReturned) r.state = "indeterminate";
139
+ } finally {
140
+ if (cwd && (!r.spawnAttempts || r.terminationConfirmed)) {
141
+ try { const s = await fs.lstat(cwd); if (s.dev !== owned.dev || s.ino !== owned.ino || !s.isDirectory()) throw Error("replaced"); await fs.rmdir(cwd); }
142
+ catch { r.state = "indeterminate"; r.reasons.push("fixture-workspace-cleanup-required"); }
143
+ }
144
+ }
145
+ return copyInertJson(r);
146
+ }