@orkastery/cli 0.5.6 → 0.6.1

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 (396) hide show
  1. package/adapters/README.md +18 -0
  2. package/adapters/claude-code/.claude-plugin/plugin.json +1 -0
  3. package/adapters/claude-code/README.md +37 -5
  4. package/adapters/claude-code/agents/ork-check.md +1 -0
  5. package/adapters/claude-code/agents/ork-go.md +1 -0
  6. package/adapters/claude-code/agents/ork-goal.md +1 -0
  7. package/adapters/claude-code/agents/ork-master.md +1 -0
  8. package/adapters/claude-code/agents/ork-plan.md +1 -0
  9. package/adapters/claude-code/agents/ork-ship.md +1 -0
  10. package/adapters/claude-code/hooks/hooks.json +2 -1
  11. package/adapters/claude-code/hooks/ork-guard.js +15 -1
  12. package/adapters/claude-code/hooks/ork-resposta.js +91 -0
  13. package/adapters/claude-code/hooks/ork-sensor.js +18 -2
  14. package/adapters/claude-code/hooks/quota-signal.js +14 -0
  15. package/adapters/claude-code/mods/ork-mensageiro/.claude-plugin/plugin.json +14 -0
  16. package/adapters/claude-code/mods/ork-mensageiro/hooks/caixa.ts +215 -0
  17. package/adapters/claude-code/mods/ork-mensageiro/hooks/hooks.json +3 -0
  18. package/adapters/claude-code/mods/ork-mensageiro/hooks/painel.tsx +165 -0
  19. package/adapters/claude-code/mods/ork-mensageiro/hooks/recepcao.ts +256 -0
  20. package/adapters/claude-code/mods/ork-mensageiro/hooks/register.tsx +481 -0
  21. package/adapters/claude-code/mods/ork-mensageiro/orkastery-mod.json +5 -0
  22. package/adapters/claude-code/mods/ork-mensageiro/types/index.d.ts +85 -0
  23. package/adapters/claude-code/mods/ork-pista/hooks/ci.ts +9 -5
  24. package/adapters/claude-code/mods/ork-ritual/.claude-plugin/plugin.json +14 -0
  25. package/adapters/claude-code/mods/ork-ritual/hooks/hooks.json +3 -0
  26. package/adapters/claude-code/mods/ork-ritual/hooks/nota.ts +88 -0
  27. package/adapters/claude-code/mods/ork-ritual/hooks/register.tsx +494 -0
  28. package/adapters/claude-code/mods/ork-ritual/hooks/replay.tsx +271 -0
  29. package/adapters/claude-code/mods/ork-ritual/hooks/ritual.ts +218 -0
  30. package/adapters/claude-code/mods/ork-ritual/orkastery-mod.json +5 -0
  31. package/adapters/claude-code/mods/ork-ritual/types/index.d.ts +106 -0
  32. package/adapters/claude-code/mods/ork-sentinela/hooks/vigia.ts +1 -1
  33. package/adapters/codex/skills/ork/SKILL.md +2 -0
  34. package/adapters/hermes/README.md +64 -1
  35. package/adapters/hermes/bin/ork-conhecimento-colecoes.sh +14 -0
  36. package/adapters/hermes/bin/ork-conhecimento-listar.sh +15 -0
  37. package/adapters/hermes/bin/ork-conhecimento-mostrar.sh +16 -0
  38. package/adapters/hermes/bin/ork-conhecimento-verificar.sh +15 -0
  39. package/adapters/hermes/bin/ork-vigia-batida.py +127 -0
  40. package/adapters/hermes/hermes.plugin.json +4 -0
  41. package/adapters/hermes/hitl-ingress/__init__.py +101 -8
  42. package/adapters/hermes/skills/orkastery-devmaster/SKILL.md +2 -2
  43. package/adapters/openclaw/README.md +81 -9
  44. package/adapters/openclaw/bin/ork-vigia-batida.js +86 -0
  45. package/adapters/openclaw/dist/hitl-ingress.js +106 -9
  46. package/adapters/openclaw/dist/index.js +86 -0
  47. package/adapters/openclaw/openclaw.plugin.json +34 -4
  48. package/adapters/openclaw/package.json +2 -2
  49. package/adapters/openclaw/src/hitl-ingress.ts +103 -9
  50. package/adapters/openclaw/src/index.ts +94 -0
  51. package/assets/adapters-en/claude-code/README.md +403 -0
  52. package/assets/adapters-en/claude-code/agents/ork-check.md +33 -0
  53. package/assets/adapters-en/claude-code/agents/ork-go.md +33 -0
  54. package/assets/adapters-en/claude-code/agents/ork-goal.md +34 -0
  55. package/assets/adapters-en/claude-code/agents/ork-master.md +35 -0
  56. package/assets/adapters-en/claude-code/agents/ork-plan.md +33 -0
  57. package/assets/adapters-en/claude-code/agents/ork-ship.md +33 -0
  58. package/assets/adapters-en/claude-code/commands/check.md +46 -0
  59. package/assets/adapters-en/claude-code/commands/go.md +46 -0
  60. package/assets/adapters-en/claude-code/commands/goal.md +55 -0
  61. package/assets/adapters-en/claude-code/commands/master.md +54 -0
  62. package/assets/adapters-en/claude-code/commands/onboarding.md +22 -0
  63. package/assets/adapters-en/claude-code/commands/ork.md +126 -0
  64. package/assets/adapters-en/claude-code/commands/plan.md +47 -0
  65. package/assets/adapters-en/claude-code/commands/ship.md +45 -0
  66. package/assets/adapters-en/codex/skills/ork/SKILL.md +85 -0
  67. package/assets/adapters-en/hermes/README.md +273 -0
  68. package/assets/adapters-en/hermes/skills/orkastery-devmaster/SKILL.md +169 -0
  69. package/assets/references-en/README.md +25 -0
  70. package/assets/references-en/code-review-axes.md +82 -0
  71. package/assets/references-en/definition-of-done.md +74 -0
  72. package/assets/references-en/performance-checklist.md +53 -0
  73. package/assets/references-en/security-checklist.md +101 -0
  74. package/assets/references-en/testing-patterns.md +51 -0
  75. package/assets/security-scan/defaults/.golangci.yml +2 -0
  76. package/assets/security-scan/defaults/eslint.config.mjs +11 -0
  77. package/assets/security-scan/defaults/gitleaks.toml +3 -0
  78. package/assets/security-scan/defaults/ruff.toml +1 -0
  79. package/assets/security-scan/tools-agil.json +87 -0
  80. package/assets/security-scan/tools.json +625 -0
  81. package/assets/skills-en/core/onboarding/SKILL.md +58 -0
  82. package/assets/skills-en/core/orkastery-bootstrap/SKILL.md +119 -0
  83. package/assets/skills-en/core/thread-state/SKILL.md +88 -0
  84. package/assets/skills-en/governance/decision-triage/SKILL.md +65 -0
  85. package/assets/skills-en/governance/narrative-guardian/SKILL.md +66 -0
  86. package/assets/skills-en/governance/roadmap-keeper/SKILL.md +60 -0
  87. package/assets/skills-en/governance/scope-check-capability-map/SKILL.md +71 -0
  88. package/assets/skills-en/observability/thread-tracing/SKILL.md +63 -0
  89. package/assets/skills-en/phases/check-quality/SKILL.md +103 -0
  90. package/assets/skills-en/phases/go-implementation/SKILL.md +78 -0
  91. package/assets/skills-en/phases/goal-definition/SKILL.md +69 -0
  92. package/assets/skills-en/phases/master-metrics/SKILL.md +72 -0
  93. package/assets/skills-en/phases/plan-specification/SKILL.md +67 -0
  94. package/assets/skills-en/phases/security-remediation/SKILL.md +70 -0
  95. package/assets/skills-en/phases/ship-release/SKILL.md +71 -0
  96. package/assets/skills-en/reviewers/code-reviewer/SKILL.md +61 -0
  97. package/assets/skills-en/reviewers/security-auditor/SKILL.md +93 -0
  98. package/assets/skills-en/reviewers/test-engineer/SKILL.md +66 -0
  99. package/assets/skills-en/reviewers/web-performance-auditor/SKILL.md +63 -0
  100. package/dist/adapters/claude-bg.js +123 -9
  101. package/dist/adapters/codex-controller-worker.js +12 -5
  102. package/dist/adapters/codex-events.js +16 -4
  103. package/dist/adapters/codex.js +3 -1
  104. package/dist/adapters/grok-events.js +227 -0
  105. package/dist/adapters/grok-mcp-servidor.js +612 -0
  106. package/dist/adapters/grok-mcp.js +178 -0
  107. package/dist/adapters/grok.js +686 -0
  108. package/dist/agendador-cpu.js +92 -0
  109. package/dist/atualizar-branch.js +245 -0
  110. package/dist/auditoria.js +13 -11
  111. package/dist/auditrun.js +51 -46
  112. package/dist/autorizacao-duravel.js +327 -0
  113. package/dist/board.js +94 -40
  114. package/dist/branch-de-estado.js +23 -10
  115. package/dist/busca-semantica.js +7 -5
  116. package/dist/catalogo.js +2 -1
  117. package/dist/changelog-reconciliacao.js +290 -0
  118. package/dist/check-agil.js +528 -0
  119. package/dist/check-modo.js +41 -0
  120. package/dist/check-paralelo.js +238 -0
  121. package/dist/ci.js +248 -45
  122. package/dist/ciclos.js +8 -6
  123. package/dist/claim-lint.js +12 -8
  124. package/dist/claims-conferir.js +47 -39
  125. package/dist/claims.js +10 -9
  126. package/dist/company-brain-client.js +10 -3
  127. package/dist/company-brain-migration.js +11 -5
  128. package/dist/conducao-batida.js +759 -0
  129. package/dist/conducao-sentinela.js +25 -8
  130. package/dist/conducao.js +89 -29
  131. package/dist/conhecimento-consulta.js +270 -0
  132. package/dist/conhecimento.js +111 -87
  133. package/dist/conta-esgotada.js +127 -0
  134. package/dist/dado-privado.js +816 -0
  135. package/dist/decisao-autonoma.js +5 -4
  136. package/dist/demo.js +14 -12
  137. package/dist/dependencias-da-worktree.js +25 -0
  138. package/dist/divida.js +38 -36
  139. package/dist/docs.js +89 -89
  140. package/dist/doctor-forja.js +343 -0
  141. package/dist/doctor-pulse-cron.js +113 -1
  142. package/dist/doctor.js +164 -6
  143. package/dist/dsn-da-memoria.js +417 -0
  144. package/dist/entrega-por-mod.js +503 -0
  145. package/dist/entrega-pr.js +154 -54
  146. package/dist/esgotamentos.js +13 -11
  147. package/dist/estado-thread.js +21 -18
  148. package/dist/evalrunner.js +35 -32
  149. package/dist/fabrica-batida.js +34 -21
  150. package/dist/fabrica-estado.js +33 -21
  151. package/dist/fix.js +21 -19
  152. package/dist/forja/adaptador.js +6 -0
  153. package/dist/forja/ambiente-de-ci.js +27 -0
  154. package/dist/forja/api.js +256 -0
  155. package/dist/forja/cache.js +89 -0
  156. package/dist/forja/cli.js +60 -0
  157. package/dist/forja/contrato.js +9 -0
  158. package/dist/forja/endereco.js +43 -0
  159. package/dist/forja/estado.js +22 -0
  160. package/dist/forja/executor.js +73 -0
  161. package/dist/forja/github/ci.js +95 -0
  162. package/dist/forja/github/dono-da-rede.js +18 -0
  163. package/dist/forja/github/metadados.js +15 -0
  164. package/dist/forja/github/pedidos.js +200 -0
  165. package/dist/forja/gitlab/ci.js +141 -0
  166. package/dist/forja/gitlab/consultas.js +59 -0
  167. package/dist/forja/gitlab/dono-da-rede.js +43 -0
  168. package/dist/forja/gitlab/metadados.js +15 -0
  169. package/dist/forja/gitlab/pedidos-abertos.js +158 -0
  170. package/dist/forja/gitlab/pedidos.js +141 -0
  171. package/dist/forja/produtor.js +32 -0
  172. package/dist/forja/registro.js +31 -0
  173. package/dist/forja/resolver.js +121 -0
  174. package/dist/forja/texto.js +13 -0
  175. package/dist/forja/versao.js +71 -0
  176. package/dist/forja.js +123 -80
  177. package/dist/handoff.js +23 -13
  178. package/dist/hitl-contract.js +46 -43
  179. package/dist/hitl-curto.js +37 -25
  180. package/dist/hitl-gates.js +13 -11
  181. package/dist/hitl-local-receipt.js +51 -0
  182. package/dist/hitl-local.js +11 -2
  183. package/dist/hitl-lock.js +5 -11
  184. package/dist/hitl-public-receipt.js +11 -0
  185. package/dist/hitl-resumo.js +44 -1
  186. package/dist/hitl-sessions.js +62 -9
  187. package/dist/hitl-texto-livre.js +7 -4
  188. package/dist/hitl.js +34 -31
  189. package/dist/horario.js +20 -1
  190. package/dist/hosts.js +104 -134
  191. package/dist/index.js +864 -554
  192. package/dist/indice-vetorial.js +14 -12
  193. package/dist/init.js +11 -2
  194. package/dist/integracoes-locais.js +4 -0
  195. package/dist/intelligence-graph-cli.js +123 -69
  196. package/dist/intelligence-graph-query.js +37 -22
  197. package/dist/leases.js +19 -18
  198. package/dist/ledger-stats.js +17 -16
  199. package/dist/ledger.js +31 -1
  200. package/dist/licoes.js +12 -11
  201. package/dist/locale-accounts.js +228 -0
  202. package/dist/locale-adapter.js +291 -0
  203. package/dist/locale-ajuda.js +86 -10
  204. package/dist/locale-audit.js +199 -0
  205. package/dist/locale-claims.js +213 -0
  206. package/dist/locale-conducao.js +138 -0
  207. package/dist/locale-conhecimento.js +181 -0
  208. package/dist/locale-decisao.js +205 -0
  209. package/dist/locale-docs.js +230 -0
  210. package/dist/locale-estado.js +101 -0
  211. package/dist/locale-forja.js +288 -0
  212. package/dist/locale-gerados.js +5 -0
  213. package/dist/locale-grafo.js +129 -0
  214. package/dist/locale-master.js +83 -0
  215. package/dist/locale-network.js +524 -0
  216. package/dist/locale-operacao-restante.js +931 -0
  217. package/dist/locale-projetos.js +100 -0
  218. package/dist/locale-prompts.js +194 -0
  219. package/dist/locale-pulse.js +140 -2
  220. package/dist/locale-reservas.js +87 -0
  221. package/dist/locale-retry.js +183 -6
  222. package/dist/locale-security-scan.js +69 -0
  223. package/dist/locale-setup.js +192 -0
  224. package/dist/locale-ship.js +177 -66
  225. package/dist/locale-thread.js +149 -5
  226. package/dist/locale-verify.js +80 -4
  227. package/dist/locale-worktree.js +246 -0
  228. package/dist/locale.js +553 -9
  229. package/dist/maestro-runtime.js +22 -3
  230. package/dist/manifest.js +293 -12
  231. package/dist/maquina-ociosa.js +211 -0
  232. package/dist/maquina.js +10 -5
  233. package/dist/master-nota-local.js +200 -0
  234. package/dist/master-nota.js +10 -2
  235. package/dist/master.js +118 -41
  236. package/dist/mcp-conhecimento.js +52 -0
  237. package/dist/mcp-experiencia.js +26 -5
  238. package/dist/mcp-install.js +90 -15
  239. package/dist/mcp-noctum.js +101 -0
  240. package/dist/mcp-server.js +116 -22
  241. package/dist/mcp-ship-transporte.js +977 -0
  242. package/dist/mcp-ship.js +68 -24
  243. package/dist/mcp-transportes.js +17 -0
  244. package/dist/memoria.js +63 -58
  245. package/dist/merge-da-entrega.js +85 -0
  246. package/dist/modos-migracao.js +8 -6
  247. package/dist/modos.js +17 -18
  248. package/dist/network-roadmap.js +94 -54
  249. package/dist/noctum-aceite.js +929 -0
  250. package/dist/noctum-arco.js +540 -33
  251. package/dist/noctum-capacidade.js +3 -3
  252. package/dist/noctum-changelog.js +399 -0
  253. package/dist/noctum-cli.js +539 -0
  254. package/dist/noctum-coordenacao.js +1225 -0
  255. package/dist/noctum-entrega.js +1217 -0
  256. package/dist/noctum-envelope.js +253 -17
  257. package/dist/noctum-execucao.js +100 -24
  258. package/dist/noctum-fechamento.js +416 -0
  259. package/dist/noctum-fila.js +49 -10
  260. package/dist/noctum-guarda.js +333 -0
  261. package/dist/noctum-integracao.js +507 -0
  262. package/dist/noctum-maquinas.js +387 -0
  263. package/dist/noctum-medicao.js +450 -0
  264. package/dist/noctum-modo.js +390 -0
  265. package/dist/noctum-pergunta-canal.js +96 -0
  266. package/dist/noctum-pergunta.js +115 -0
  267. package/dist/noctum-provas.js +838 -0
  268. package/dist/noctum-reconciliacao.js +594 -0
  269. package/dist/noctum-rede.js +102 -0
  270. package/dist/noctum-registro.js +215 -0
  271. package/dist/noctum-relatorio.js +861 -0
  272. package/dist/noctum-resposta.js +207 -0
  273. package/dist/noctum-retomada-rede.js +71 -0
  274. package/dist/noctum-sessoes-em-voo.js +93 -0
  275. package/dist/noctum-supervisao.js +649 -0
  276. package/dist/noctum-terminal.js +459 -0
  277. package/dist/noctum-transporte.js +655 -0
  278. package/dist/noctum-vigia.js +679 -0
  279. package/dist/ocupacao.js +3 -1
  280. package/dist/onboarding.js +16 -5
  281. package/dist/orkmind.js +39 -23
  282. package/dist/orquestracao.js +31 -35
  283. package/dist/parado-no-condutor.js +160 -139
  284. package/dist/phase.js +96 -42
  285. package/dist/policies.js +33 -0
  286. package/dist/postura-local.js +11 -6
  287. package/dist/preflight.js +2 -1
  288. package/dist/procedencia.js +9 -14
  289. package/dist/projeto-alvo.js +59 -20
  290. package/dist/prompts.js +136 -42
  291. package/dist/prova-minima.js +5 -6
  292. package/dist/pulse-cadencia.js +5 -4
  293. package/dist/pulse-consentimento.js +53 -1
  294. package/dist/pulse-delivery.js +143 -27
  295. package/dist/pulse-resposta.js +368 -4
  296. package/dist/pulse.js +136 -3
  297. package/dist/ratelimit.js +13 -4
  298. package/dist/recall.js +14 -13
  299. package/dist/rede-adesao.js +13 -8
  300. package/dist/rede-forja.js +108 -24
  301. package/dist/rede-status.js +63 -45
  302. package/dist/rede.js +86 -46
  303. package/dist/retomada-checkpoint.js +101 -0
  304. package/dist/retry.js +326 -78
  305. package/dist/roadmap-reservas.js +32 -32
  306. package/dist/runtime-ambiente.js +5 -0
  307. package/dist/runtime-context.js +279 -85
  308. package/dist/runtime-escada.js +429 -0
  309. package/dist/runtime-profiles.js +130 -63
  310. package/dist/runtimes.js +42 -5
  311. package/dist/saida-segura.js +3 -2
  312. package/dist/security-scan/agil.js +366 -0
  313. package/dist/security-scan/cli.js +76 -0
  314. package/dist/security-scan/compare.js +149 -0
  315. package/dist/security-scan/config.js +93 -0
  316. package/dist/security-scan/detect.js +97 -0
  317. package/dist/security-scan/executor.js +196 -0
  318. package/dist/security-scan/guard.js +180 -0
  319. package/dist/security-scan/imagem-de-lint.js +96 -0
  320. package/dist/security-scan/normalize/actionlint.js +12 -0
  321. package/dist/security-scan/normalize/eslint.js +12 -0
  322. package/dist/security-scan/normalize/gitleaks.js +13 -0
  323. package/dist/security-scan/normalize/golangci-lint.js +13 -0
  324. package/dist/security-scan/normalize/govulncheck.js +36 -0
  325. package/dist/security-scan/normalize/index.js +25 -0
  326. package/dist/security-scan/normalize/npm-audit.js +30 -0
  327. package/dist/security-scan/normalize/osv-scanner.js +32 -0
  328. package/dist/security-scan/normalize/oxlint.js +51 -0
  329. package/dist/security-scan/normalize/pip-audit.js +16 -0
  330. package/dist/security-scan/normalize/ruff.js +12 -0
  331. package/dist/security-scan/normalize/semgrep.js +20 -0
  332. package/dist/security-scan/normalize/trivy.js +33 -0
  333. package/dist/security-scan/normalize/util.js +204 -0
  334. package/dist/security-scan/normalize/zizmor.js +19 -0
  335. package/dist/security-scan/report.js +156 -0
  336. package/dist/security-scan/run.js +448 -0
  337. package/dist/security-scan/tools.js +62 -0
  338. package/dist/security-scan/trabalhador.js +74 -0
  339. package/dist/security-scan/types.js +15 -0
  340. package/dist/session-events.js +19 -2
  341. package/dist/session-watcher-claude.js +74 -19
  342. package/dist/session-watcher-grok.js +425 -0
  343. package/dist/session-watcher.js +174 -13
  344. package/dist/sessoes-contas.js +42 -5
  345. package/dist/sessoes-inventario.js +1 -1
  346. package/dist/sessoes.js +25 -23
  347. package/dist/setup.js +67 -66
  348. package/dist/ship-publicar.js +263 -92
  349. package/dist/ship.js +160 -17
  350. package/dist/superficie.js +14 -11
  351. package/dist/thread-close.js +9 -9
  352. package/dist/thread.js +121 -18
  353. package/dist/tokens.js +15 -11
  354. package/dist/trabalho-thread.js +297 -0
  355. package/dist/transporte-do-canal.js +189 -0
  356. package/dist/types.js +12 -1
  357. package/dist/util.js +5 -2
  358. package/dist/varredura-acoes.js +500 -0
  359. package/dist/verify.js +491 -32
  360. package/dist/worktree-local.js +12 -10
  361. package/dist/worktree.js +81 -85
  362. package/eval/casos/check-quality.json +77 -0
  363. package/eval/casos/security-auditor.json +148 -0
  364. package/eval/casos/security-remediation.json +161 -0
  365. package/eval/fixtures/security-scan/README.md +8 -0
  366. package/eval/fixtures/security-scan/go/.github/workflows/ci.yml +12 -0
  367. package/eval/fixtures/security-scan/go/Dockerfile +4 -0
  368. package/eval/fixtures/security-scan/go/README.md +4 -0
  369. package/eval/fixtures/security-scan/go/config.go +4 -0
  370. package/eval/fixtures/security-scan/go/go.mod +5 -0
  371. package/eval/fixtures/security-scan/go/go.sum +2 -0
  372. package/eval/fixtures/security-scan/go/main.go +31 -0
  373. package/eval/fixtures/security-scan/js/.github/workflows/ci.yml +12 -0
  374. package/eval/fixtures/security-scan/js/Dockerfile +3 -0
  375. package/eval/fixtures/security-scan/js/README.md +4 -0
  376. package/eval/fixtures/security-scan/js/package-lock.json +844 -0
  377. package/eval/fixtures/security-scan/js/package.json +10 -0
  378. package/eval/fixtures/security-scan/js/src/config.js +4 -0
  379. package/eval/fixtures/security-scan/js/src/server.js +17 -0
  380. package/eval/fixtures/security-scan/js/src/util.ts +4 -0
  381. package/eval/fixtures/security-scan/js/tsconfig.json +4 -0
  382. package/eval/fixtures/security-scan/python/.github/workflows/ci.yml +12 -0
  383. package/eval/fixtures/security-scan/python/Dockerfile +3 -0
  384. package/eval/fixtures/security-scan/python/README.md +4 -0
  385. package/eval/fixtures/security-scan/python/app/__init__.py +0 -0
  386. package/eval/fixtures/security-scan/python/app/config.py +2 -0
  387. package/eval/fixtures/security-scan/python/app/main.py +19 -0
  388. package/eval/fixtures/security-scan/python/requirements.txt +1 -0
  389. package/package.json +10 -5
  390. package/references/security-checklist.md +3 -3
  391. package/skills/README.md +1 -0
  392. package/skills/core/orkastery-bootstrap/SKILL.md +2 -2
  393. package/skills/phases/check-quality/SKILL.md +16 -0
  394. package/skills/phases/go-implementation/SKILL.md +6 -0
  395. package/skills/phases/security-remediation/SKILL.md +70 -0
  396. package/skills/reviewers/security-auditor/SKILL.md +25 -0
@@ -33,19 +33,27 @@ var __importStar = (this && this.__importStar) || (function () {
33
33
  };
34
34
  })();
35
35
  Object.defineProperty(exports, "__esModule", { value: true });
36
- exports.PRAZO_PARA_AUTORIZAR_MIN = exports.TEXTO_DE_REVOGACAO = exports.textoDeAutorizacao = exports.enderecoDoArco = exports.ALVO_DO_NOCTUM = exports.CONTRATO_ARCO = void 0;
36
+ exports.MOTIVOS_DE_ENCERRAMENTO = exports.PRAZO_PARA_AUTORIZAR_MIN = exports.TEXTO_DE_REVOGACAO = exports.textoDeAutorizacao = exports.enderecoDoArco = exports.ALVO_DO_NOCTUM = exports.CONTRATO_ARCO = void 0;
37
37
  exports.contextoDoArco = contextoDoArco;
38
38
  exports.hashDaRevogacao = hashDaRevogacao;
39
39
  exports.proporEnvelope = proporEnvelope;
40
+ exports.codigosDosArcos = codigosDosArcos;
41
+ exports.pedirRevogacao = pedirRevogacao;
42
+ exports.perguntasDoArco = perguntasDoArco;
40
43
  exports.autorizarPeloIngresso = autorizarPeloIngresso;
41
44
  exports.revogarPeloIngresso = revogarPeloIngresso;
45
+ exports.autorizarPelaResposta = autorizarPelaResposta;
46
+ exports.revogarPelaResposta = revogarPelaResposta;
42
47
  exports.criarDialogoDoArco = criarDialogoDoArco;
43
48
  exports.idDaDecisao = idDaDecisao;
44
49
  exports.enfileirarDecisao = enfileirarDecisao;
45
50
  exports.lerArco = lerArco;
51
+ exports.lerArcoComProposta = lerArcoComProposta;
46
52
  exports.conferirAntesDoEfeito = conferirAntesDoEfeito;
47
53
  exports.reservarDespacho = reservarDespacho;
54
+ exports.encerrarArco = encerrarArco;
48
55
  exports.concluirDespacho = concluirDespacho;
56
+ exports.versoesAutorizadas = versoesAutorizadas;
49
57
  /**
50
58
  * RM-058, fatia N1: o estado do arco #Noctum e o ingresso do envelope.
51
59
  *
@@ -82,6 +90,8 @@ const hitl_local_receipt_1 = require("./hitl-local-receipt");
82
90
  const hitl_native_1 = require("./hitl-native");
83
91
  const hitl_public_receipt_1 = require("./hitl-public-receipt");
84
92
  const noctum_envelope_1 = require("./noctum-envelope");
93
+ const noctum_pergunta_1 = require("./noctum-pergunta");
94
+ const pulse_consentimento_1 = require("./pulse-consentimento");
85
95
  const thread_1 = require("./thread");
86
96
  exports.CONTRATO_ARCO = 'ork.noctum-arco/v1';
87
97
  /**
@@ -100,6 +110,13 @@ exports.TEXTO_DE_REVOGACAO = 'revogar';
100
110
  exports.PRAZO_PARA_AUTORIZAR_MIN = 60;
101
111
  /** O diálogo do host espera no máximo isto, e nunca além do prazo da proposta. */
102
112
  const ESPERA_DO_DIALOGO_MS = 300000;
113
+ /**
114
+ * RM-058 N6 (D2 e D8 da ork-rm058n6): por que o arco fechou. O fechamento é ato do núcleo, não do dono: ele só registra que
115
+ * a hora, o teto ou a fila acabaram, e que a revogação do dono já foi tratada. RM-058, integração (D7 da ork-rm058integra):
116
+ * `supervisao` é a parada da supervisão da N4 (o supervisor que falhou, o teto, a violação de custo ou o ambiente do
117
+ * despacho), que o fechamento lê do log dela depois do revogado e da hora e antes do custo.
118
+ */
119
+ exports.MOTIVOS_DE_ENCERRAMENTO = ['revogado', 'hora', 'custo', 'fila-vazia', 'supervisao'];
103
120
  const ZERO = '0'.repeat(64);
104
121
  const ARQUIVO = 'arco.jsonl';
105
122
  const sha = (s) => (0, node_crypto_1.createHash)('sha256').update(s).digest('hex');
@@ -266,11 +283,19 @@ async function comLockDoArcoAssincrono(raiz, arco, executar) {
266
283
  }
267
284
  }
268
285
  }
286
+ /** A identidade de uma prova do dono: cada uma vale para um ato só. */
287
+ function chaveDaProva(i) {
288
+ if (i.origem === 'mcp-local')
289
+ return `mcp-local:${i.solicitacao}`;
290
+ if (i.origem === 'terminal')
291
+ return `terminal:${i.solicitacao}`;
292
+ return `${i.origem}:${i.mensagem}`;
293
+ }
269
294
  function derivar(linhas) {
270
295
  const d = { propostas: new Map(), ultima: null, autorizacao: null, revogacao: null, decisoes: [], usadas: new Set(),
271
- despachos: new Map() };
296
+ despachos: new Map(), encerramento: null, revogacoesPedidas: [] };
272
297
  const usar = (i) => {
273
- const prova = i.origem === 'mcp-local' ? `mcp-local:${i.solicitacao}` : `${i.origem}:${i.mensagem}`;
298
+ const prova = chaveDaProva(i);
274
299
  // Uma prova do dono vale para um ato só: reaproveitada noutra linha, o log foi emendado à mão.
275
300
  if (d.usadas.has(prova))
276
301
  throw adulterado('a mesma prova do dono aparece em dois atos do log');
@@ -294,8 +319,23 @@ function derivar(linhas) {
294
319
  Object.assign(r, { desfecho: 'recusado', motivo: e.motivo, concluidoEm: em });
295
320
  continue;
296
321
  }
322
+ if (e.tipo === 'encerrado') {
323
+ // O fechamento é um só e vem depois da autorização: outra linha assim só existe num log emendado à mão.
324
+ if (!d.autorizacao || d.encerramento)
325
+ throw adulterado('encerramento sem autorização ou repetido');
326
+ d.encerramento = { motivo: e.motivo, detalhe: e.detalhe, em, versao: d.autorizacao.versao, envelopeSha256: d.autorizacao.envelopeSha256 };
327
+ continue;
328
+ }
329
+ if (e.tipo === 'revogacao_pedida') {
330
+ // Pergunta de revogação sem autorização antes dela só existe num log emendado à mão.
331
+ if (!d.autorizacao)
332
+ throw adulterado('pergunta de revogação sem autorização');
333
+ d.revogacoesPedidas.push({ codigo: e.codigo, prazo: e.prazo, em });
334
+ continue;
335
+ }
297
336
  if (e.tipo === 'proposto') {
298
- const p = { versao: e.versao, envelopeSha256: e.envelopeSha256, conteudo: e.conteudo, prazo: e.prazo };
337
+ const p = { versao: e.versao, envelopeSha256: e.envelopeSha256, conteudo: e.conteudo, prazo: e.prazo, em,
338
+ ...(typeof e.codigo === 'string' ? { codigo: e.codigo } : {}) };
299
339
  if ((0, noctum_envelope_1.hashDoEnvelope)(p.conteudo) !== p.envelopeSha256)
300
340
  throw adulterado(`proposta ${p.versao} com hash que não confere`);
301
341
  d.propostas.set(p.versao, p);
@@ -304,6 +344,8 @@ function derivar(linhas) {
304
344
  else if (e.tipo === 'autorizado') {
305
345
  if (d.propostas.get(e.versao)?.envelopeSha256 !== e.envelopeSha256)
306
346
  throw adulterado(`autorização da versão ${e.versao} sem proposta`);
347
+ if (d.encerramento)
348
+ throw adulterado(`autorização da versão ${e.versao} depois do encerramento`);
307
349
  d.autorizacao = { versao: e.versao, envelopeSha256: e.envelopeSha256, ingresso: e.ingresso };
308
350
  usar(e.ingresso);
309
351
  }
@@ -321,6 +363,8 @@ function situacao(d, quando) {
321
363
  return 'revogado';
322
364
  if (!d.autorizacao)
323
365
  return 'proposto';
366
+ if (d.encerramento)
367
+ return 'encerrado';
324
368
  const janela = d.propostas.get(d.autorizacao.versao).conteudo.janela;
325
369
  const agora = Date.parse(quando);
326
370
  if (agora < Date.parse(janela.inicio))
@@ -329,13 +373,19 @@ function situacao(d, quando) {
329
373
  }
330
374
  function estadoDe(arco, d, quando) {
331
375
  const vigente = d.autorizacao ? d.propostas.get(d.autorizacao.versao) : null;
332
- const pendente = d.ultima && !d.revogacao && d.ultima.versao !== d.autorizacao?.versao &&
376
+ const pendente = d.ultima && !d.revogacao && !d.encerramento && d.ultima.versao !== d.autorizacao?.versao &&
333
377
  Date.parse(quando) < Date.parse(d.ultima.prazo) ? d.ultima : null;
334
- return { contrato: exports.CONTRATO_ARCO, arco, situacao: situacao(d, quando), versao: vigente?.versao ?? null,
378
+ const sit = situacao(d, quando);
379
+ // RM-057 (resposta curta): só a última pergunta de revogação espera o dono, e só com o arco ainda de pé.
380
+ const rev = d.revogacoesPedidas.at(-1);
381
+ const revogacaoPedida = rev && (sit === 'vigente' || sit === 'agendado') && Date.parse(quando) < Date.parse(rev.prazo)
382
+ ? { codigo: rev.codigo, prazo: rev.prazo, pedidaEm: rev.em } : null;
383
+ return { contrato: exports.CONTRATO_ARCO, arco, situacao: sit, versao: vigente?.versao ?? null,
335
384
  envelopeSha256: vigente?.envelopeSha256 ?? null, conteudo: vigente?.conteudo ?? null,
336
385
  autorizadoPor: d.autorizacao?.ingresso ?? null, revogadoPor: d.revogacao,
337
- propostaPendente: pendente ? { versao: pendente.versao, envelopeSha256: pendente.envelopeSha256, prazo: pendente.prazo } : null,
338
- decisoes: d.decisoes, despachos: [...d.despachos.values()], consumo: consumoDe(d) };
386
+ propostaPendente: pendente ? { versao: pendente.versao, envelopeSha256: pendente.envelopeSha256, prazo: pendente.prazo,
387
+ ...(pendente.codigo ? { codigo: pendente.codigo } : {}) } : null,
388
+ decisoes: d.decisoes, despachos: [...d.despachos.values()], consumo: consumoDe(d), encerramento: d.encerramento, revogacaoPedida };
339
389
  }
340
390
  /** D12 da ork-rm058n2: a reserva consome uma sessão até ser recusada; a concluída e a pendente contam. */
341
391
  function consumoDe(d) {
@@ -355,7 +405,7 @@ function consumoDe(d) {
355
405
  * A mesma proposta repetida devolve a que está pendente. Proposta nova sobre um envelope já
356
406
  * autorizado é emenda: o vigente continua valendo até o dono autorizar a nova versão.
357
407
  */
358
- function proporEnvelope(raiz, pedido, quando = new Date().toISOString()) {
408
+ function proporEnvelope(raiz, pedido, quando = new Date().toISOString(), emUso = []) {
359
409
  const conteudo = (0, noctum_envelope_1.validarPedidoDeAbertura)(pedido);
360
410
  const envelopeSha256 = (0, noctum_envelope_1.hashDoEnvelope)(conteudo);
361
411
  if (!(Date.parse(quando) < Date.parse(conteudo.janela.fim))) {
@@ -365,6 +415,8 @@ function proporEnvelope(raiz, pedido, quando = new Date().toISOString()) {
365
415
  const log = lerLog(raiz, conteudo.arco), d = derivar(log.linhas);
366
416
  if (d.revogacao)
367
417
  throw new noctum_envelope_1.RecusaDoArco('noctum.efeito.revogado', 'arco revogado é terminal; abra outro arco');
418
+ if (d.encerramento)
419
+ throw new noctum_envelope_1.RecusaDoArco('noctum.efeito.encerrado', `arco encerrado (${d.encerramento.motivo}) é terminal; abra outro arco`);
368
420
  const resumo = (0, noctum_envelope_1.resumoDoEnvelope)(conteudo, envelopeSha256);
369
421
  const pendente = estadoDe(conteudo.arco, d, quando).propostaPendente;
370
422
  if (pendente?.envelopeSha256 === envelopeSha256)
@@ -376,9 +428,90 @@ function proporEnvelope(raiz, pedido, quando = new Date().toISOString()) {
376
428
  exigirEmendaSemProrrogar(d, conteudo, quando);
377
429
  const versao = (d.ultima?.versao ?? 0) + 1;
378
430
  const prazo = new Date(Date.parse(quando) + exports.PRAZO_PARA_AUTORIZAR_MIN * 60000).toISOString();
379
- anexar(raiz, conteudo.arco, log, { tipo: 'proposto', versao, envelopeSha256, conteudo, prazo }, quando);
380
- return { arco: conteudo.arco, versao, envelopeSha256, prazo, resumo };
431
+ // RM-057 (resposta curta, D2): a pergunta nasce com a proposta, com um código que nenhuma outra pergunta usa agora.
432
+ const codigo = (0, pulse_consentimento_1.gerarCodigo)([...emUso, ...codigosDosArcos(raiz)]);
433
+ anexar(raiz, conteudo.arco, log, { tipo: 'proposto', versao, envelopeSha256, conteudo, prazo, codigo }, quando);
434
+ return { arco: conteudo.arco, versao, envelopeSha256, prazo, resumo, codigo };
435
+ });
436
+ }
437
+ /**
438
+ * RM-057 (resposta curta): os códigos das perguntas de todos os arcos desta raiz, para um código novo nunca repetir um
439
+ * deles. Arco que não se lê não contribui (a resolução por código recusa o ambíguo de qualquer jeito).
440
+ */
441
+ function codigosDosArcos(raiz) {
442
+ let nomes;
443
+ try {
444
+ nomes = fs.readdirSync(path.join((0, estado_thread_1.raizDoEstado)(raiz), '.orkastery', 'noctum', 'arcos')).filter(n => noctum_envelope_1.ID_DO_ARCO.test(n));
445
+ }
446
+ catch {
447
+ return [];
448
+ }
449
+ const codigos = [];
450
+ for (const arco of nomes) {
451
+ try {
452
+ const d = derivar(lerLog(raiz, arco).linhas);
453
+ for (const p of d.propostas.values())
454
+ if (p.codigo)
455
+ codigos.push(p.codigo);
456
+ for (const r of d.revogacoesPedidas)
457
+ codigos.push(r.codigo);
458
+ }
459
+ catch { /* arco ilegível não empresta código */ }
460
+ }
461
+ return codigos;
462
+ }
463
+ /**
464
+ * RM-057 (resposta curta, D10 da ork-rm057respost): abre a pergunta de revogação ao dono. Não revoga nada: quem revoga é
465
+ * a resposta dele, pela prova do canal ou do terminal. A pergunta que ainda espera o dono é devolvida, em vez de outra.
466
+ */
467
+ function pedirRevogacao(raiz, arco, quando = new Date().toISOString(), emUso = []) {
468
+ return comLockDoArco(raiz, arco, false, () => {
469
+ const log = lerLog(raiz, arco), d = derivar(log.linhas);
470
+ if (d.revogacao)
471
+ throw new noctum_envelope_1.RecusaDoArco('noctum.efeito.revogado', `o arco ${arco} já foi revogado`);
472
+ if (d.encerramento)
473
+ throw new noctum_envelope_1.RecusaDoArco('noctum.efeito.encerrado', `arco ${arco} encerrado (${d.encerramento.motivo}) é terminal`);
474
+ if (!d.autorizacao) {
475
+ throw new noctum_envelope_1.RecusaDoArco('noctum.efeito.sem-autorizacao', `o arco ${arco} nunca foi autorizado: não há o que revogar, e a proposta vence sozinha`);
476
+ }
477
+ const estado = estadoDe(arco, d, quando);
478
+ if (estado.situacao === 'expirado')
479
+ throw new noctum_envelope_1.RecusaDoArco('noctum.efeito.expirado', `o arco ${arco} já terminou; não há o que revogar`);
480
+ const base = { arco, versao: d.autorizacao.versao, envelopeSha256: d.autorizacao.envelopeSha256 };
481
+ if (estado.revogacaoPedida)
482
+ return { ...base, codigo: estado.revogacaoPedida.codigo, prazo: estado.revogacaoPedida.prazo, nova: false };
483
+ const codigo = (0, pulse_consentimento_1.gerarCodigo)([...emUso, ...codigosDosArcos(raiz)]);
484
+ const prazo = new Date(Date.parse(quando) + exports.PRAZO_PARA_AUTORIZAR_MIN * 60000).toISOString();
485
+ anexar(raiz, arco, log, { tipo: 'revogacao_pedida', codigo, prazo }, quando);
486
+ return { ...base, codigo, prazo, nova: true };
487
+ });
488
+ }
489
+ /**
490
+ * RM-057 (resposta curta): as perguntas do arco ao dono, derivadas do log a cada leitura, com a situação de cada uma.
491
+ * Só a `pendente` aceita resposta; as outras existem para a resposta atrasada ouvir o porquê, nunca para valer.
492
+ */
493
+ function perguntasDoArco(raiz, arco, quando = new Date().toISOString()) {
494
+ const d = derivar(lerLog(raiz, arco).linhas);
495
+ const estado = estadoDe(arco, d, quando);
496
+ const terminal = d.revogacao !== null || d.encerramento !== null;
497
+ const perguntas = [];
498
+ for (const p of d.propostas.values()) {
499
+ if (!p.codigo)
500
+ continue;
501
+ const situacao = d.autorizacao?.versao === p.versao ? 'respondida'
502
+ : d.ultima?.versao !== p.versao ? 'substituida' : terminal ? 'arco-terminal'
503
+ : estado.propostaPendente?.versao === p.versao && Date.parse(quando) < Date.parse(p.conteudo.janela.fim) ? 'pendente' : 'vencida';
504
+ perguntas.push({ contrato: noctum_pergunta_1.CONTRATO_PERGUNTA_DO_ARCO, arco, ato: 'autorizar', codigo: p.codigo, versao: p.versao,
505
+ envelopeSha256: p.envelopeSha256, criadaEm: p.em, prazo: p.prazo, conteudo: p.conteudo, situacao });
506
+ }
507
+ const vigente = d.autorizacao ? d.propostas.get(d.autorizacao.versao) ?? null : null;
508
+ d.revogacoesPedidas.forEach((r, i) => {
509
+ const situacao = d.revogacao ? 'respondida' : i < d.revogacoesPedidas.length - 1 ? 'substituida'
510
+ : d.encerramento ? 'arco-terminal' : estado.revogacaoPedida?.codigo === r.codigo ? 'pendente' : 'vencida';
511
+ perguntas.push({ contrato: noctum_pergunta_1.CONTRATO_PERGUNTA_DO_ARCO, arco, ato: 'revogar', codigo: r.codigo, versao: vigente?.versao ?? null,
512
+ envelopeSha256: vigente?.envelopeSha256 ?? null, criadaEm: r.em, prazo: r.prazo, conteudo: vigente?.conteudo ?? null, situacao });
381
513
  });
514
+ return perguntas;
382
515
  }
383
516
  /**
384
517
  * D7: emenda é versão nova com prova nova, mas não prorroga nada. Ela mantém o início, não passa do
@@ -394,11 +527,20 @@ function exigirEmendaSemProrrogar(d, conteudo, quando) {
394
527
  if (Date.parse(conteudo.janela.inicio) !== Date.parse(janela.inicio) || Date.parse(conteudo.janela.fim) > Date.parse(janela.fim)) {
395
528
  throw new noctum_envelope_1.RecusaDoArco('noctum.envelope.invalido', 'emenda mantém o início e não prorroga o fim do envelope autorizado');
396
529
  }
530
+ // RM-058 N3 (A4 da revisão da ork-rm058n3): a emenda não troca o contrato entre a `v3` e as outras. O consumo e as sessões de
531
+ // uma máquina só não estão no ramo de coordenação, e a soma entre máquinas os perderia; o arco entre máquinas nasce assim.
532
+ const antes = d.propostas.get(d.autorizacao.versao).conteudo.contrato;
533
+ if ((antes === noctum_envelope_1.CONTRATO_ENVELOPE_V3) !== (conteudo.contrato === noctum_envelope_1.CONTRATO_ENVELOPE_V3)) {
534
+ throw new noctum_envelope_1.RecusaDoArco('noctum.envelope.invalido', `a emenda não troca o contrato de ${antes} para ${conteudo.contrato}: ` +
535
+ 'o arco entre máquinas começa como tal, e o de uma máquina segue assim; abra outro arco');
536
+ }
397
537
  }
398
538
  /** A proposta que o dono pode autorizar agora, ou a recusa tipada de por que não. */
399
539
  function propostaAutorizavel(d, envelopeSha256, quando) {
400
540
  if (d.revogacao)
401
541
  throw new noctum_envelope_1.RecusaDoArco('noctum.efeito.revogado', 'arco revogado é terminal; abra outro arco');
542
+ if (d.encerramento)
543
+ throw new noctum_envelope_1.RecusaDoArco('noctum.efeito.encerrado', `arco encerrado (${d.encerramento.motivo}) é terminal; abra outro arco`);
402
544
  if (!d.ultima)
403
545
  throw new noctum_envelope_1.RecusaDoArco('noctum.efeito.sem-autorizacao', 'arco sem proposta');
404
546
  if (d.ultima.envelopeSha256 !== envelopeSha256) {
@@ -448,7 +590,9 @@ function provarEnvelope(arco, texto, envelope, quando, amarra) {
448
590
  function dadosDoRecibo(arco, ato, envelopeSha256, versao, i) {
449
591
  return { contrato: 'ork.noctum-ingresso/v1', arco, endereco: (0, exports.enderecoDoArco)(arco), ato, envelopeSha256, versao,
450
592
  origem: i.origem, canal: i.canal, conta: i.conta, por: i.por, mensagem: i.mensagem, recebidoEm: i.recebidoEm,
451
- recibo: i.recibo, ...(i.origem === 'native' ? { native: i.native } : {}) };
593
+ recibo: i.recibo, ...(i.origem === 'native' ? { native: i.native } : {}),
594
+ // RM-057 (resposta curta, D7): a pergunta e o endereço que o gateway assinou entram no MAC do canal.
595
+ ...(i.origem === 'telegram' && i.pergunta ? { pergunta: i.pergunta } : {}) };
452
596
  }
453
597
  function lerPrivado(file) {
454
598
  const fd = fs.openSync(file, fs.constants.O_RDONLY | fs.constants.O_NOFOLLOW);
@@ -509,6 +653,8 @@ function gravarRecibo(raiz, arco, ato, envelopeSha256, versao, i) {
509
653
  function reciboConfere(raiz, arco, ato, envelopeSha256, versao, i) {
510
654
  if (i.origem === 'mcp-local')
511
655
  return 'ok';
656
+ if (i.origem === 'terminal')
657
+ return reciboDoTerminalConfere(raiz, arco, ato, envelopeSha256, versao, i);
512
658
  try {
513
659
  const nome = nomeDoRecibo(i);
514
660
  if (i.evidencia !== `.orkastery/noctum/arcos/${arco}/ingresso/${nome}`)
@@ -561,15 +707,29 @@ function confirmado(raiz, arco, quando, vale, ato) {
561
707
  throw adulterado(`a ${ato} não ficou gravada no log`);
562
708
  return estado;
563
709
  }
564
- /** Autoriza a proposta pendente pelo envelope assinado do dono. Sem prova, nada muda. */
565
- function autorizarPeloIngresso(raiz, arco, envelopeSha256, envelope, quando = new Date().toISOString()) {
710
+ /**
711
+ * Autoriza a proposta pendente pelo envelope assinado do dono. Sem prova, nada muda. `info.repetida` diz, decidido sob
712
+ * o lock do arco, que o ato já valia antes desta mensagem (o gateway não confirma de novo ao dono).
713
+ */
714
+ function autorizarPeloIngresso(raiz, arco, envelopeSha256, envelope, quando = new Date().toISOString(), info) {
566
715
  return comLockDoArco(raiz, arco, false, () => {
567
716
  const log = lerLog(raiz, arco), d = derivar(log.linhas);
568
717
  const versao = d.ultima?.envelopeSha256 === envelopeSha256 ? d.ultima.versao : 0;
569
718
  const provado = provarEnvelope(arco, (0, exports.textoDeAutorizacao)(envelopeSha256), envelope, quando, { pedidoSha256: envelopeSha256, contexto: contextoDoArco(arco, versao), prazo: d.ultima?.prazo });
570
- // A mesma autorização repetida, pela mesma mensagem, devolve o que já vale.
571
- if (d.autorizacao?.envelopeSha256 === envelopeSha256 && !d.revogacao)
572
- return estadoDe(arco, d, quando);
719
+ // A mesma autorização repetida devolve o que já vale; no arco que já acabou, a recusa tipada chega ao gateway, que
720
+ // responde ao dono, em vez de um ok mudo (RM-058 N6).
721
+ if (d.autorizacao?.envelopeSha256 === envelopeSha256 && !d.revogacao) {
722
+ const atual = estadoDe(arco, d, quando);
723
+ if (atual.situacao === 'encerrado') {
724
+ throw new noctum_envelope_1.RecusaDoArco('noctum.efeito.encerrado', `arco ${arco} encerrado (${d.encerramento?.motivo ?? 'fechado'}) é terminal; abra outro arco`);
725
+ }
726
+ if (atual.situacao === 'expirado') {
727
+ throw new noctum_envelope_1.RecusaDoArco('noctum.efeito.expirado', `o arco ${arco} terminou em ${atual.conteudo?.janela.fim ?? 'o fim da janela'}; abra outro arco`);
728
+ }
729
+ if (info)
730
+ info.repetida = true;
731
+ return atual;
732
+ }
573
733
  if (d.usadas.has(`${provado.origem}:${provado.mensagem}`))
574
734
  throw semProva('esta mensagem já provou outro ato neste arco');
575
735
  const proposta = propostaAutorizavel(d, envelopeSha256, quando);
@@ -578,13 +738,16 @@ function autorizarPeloIngresso(raiz, arco, envelopeSha256, envelope, quando = ne
578
738
  return confirmado(raiz, arco, quando, e => e.envelopeSha256 === envelopeSha256 && e.situacao !== 'revogado', 'autorização');
579
739
  });
580
740
  }
581
- /** Revoga o arco pelo envelope assinado do dono. Terminal: nenhum efeito passa depois (D7). */
582
- function revogarPeloIngresso(raiz, arco, envelope, quando = new Date().toISOString()) {
741
+ /** Revoga o arco pelo envelope assinado do dono. Terminal: nenhum efeito passa depois (D7). `info.repetida`: já valia. */
742
+ function revogarPeloIngresso(raiz, arco, envelope, quando = new Date().toISOString(), info) {
583
743
  return comLockDoArco(raiz, arco, false, () => {
584
744
  const log = lerLog(raiz, arco), d = derivar(log.linhas);
585
745
  const provado = provarEnvelope(arco, exports.TEXTO_DE_REVOGACAO, envelope, quando, { pedidoSha256: hashDaRevogacao(arco), contexto: contextoDoArco(arco, 0) });
586
- if (d.revogacao)
746
+ if (d.revogacao) {
747
+ if (info)
748
+ info.repetida = true;
587
749
  return estadoDe(arco, d, quando);
750
+ }
588
751
  if (d.usadas.has(`${provado.origem}:${provado.mensagem}`))
589
752
  throw semProva('esta mensagem já provou outro ato neste arco');
590
753
  // O recibo da revogação é auditoria e nunca é reconferido: falha nele não segura o ato de segurança, e o motivo fica na linha.
@@ -601,6 +764,274 @@ function revogarPeloIngresso(raiz, arco, envelope, quando = new Date().toISOStri
601
764
  return confirmado(raiz, arco, quando, e => e.situacao === 'revogado', 'revogação');
602
765
  }, true);
603
766
  }
767
+ // ---------------------------------------------------------------------------
768
+ // RM-057 (resposta curta, ork-rm057respost): os atos do dono pela resposta curta à pergunta do arco.
769
+ // ---------------------------------------------------------------------------
770
+ /** O domínio da subchave local que assina o recibo do terminal: o mesmo segredo do recibo local, separado por domínio. */
771
+ const DOMINIO_DO_RECIBO_DO_TERMINAL = 'ork.noctum-terminal/v1';
772
+ /** A resposta curta já lida pelo núcleo: o código da pergunta, a letra e o hash do texto do dono. */
773
+ function validarRespostaAPergunta(r) {
774
+ if (!objeto(r) || typeof r.codigo !== 'string' || !pulse_consentimento_1.CODIGO_CURTO.test(r.codigo) || !['a', 'b', 'c'].includes(String(r.letra)) ||
775
+ typeof r.respostaSha256 !== 'string' || !/^[a-f0-9]{64}$/.test(r.respostaSha256) ||
776
+ (r.numero !== undefined && (!Number.isInteger(r.numero) || r.numero < 1 || r.numero > 99))) {
777
+ throw semProva('a resposta curta chegou malformada');
778
+ }
779
+ return { codigo: r.codigo, letra: r.letra, respostaSha256: r.respostaSha256,
780
+ ...(r.numero !== undefined ? { numero: r.numero } : {}) };
781
+ }
782
+ /**
783
+ * A prova da resposta curta. Pelo canal, a MESMA `autenticarResposta` do HITL, no endereço do pulse que o gateway assinou
784
+ * (`resposta`, ou `resposta:<código>` no reply), no instante do envelope e no de agora, com o texto assinado igual ao que o
785
+ * núcleo leu. Pelo terminal, o atestado da origem da sessão, conferido por `noctum-terminal.ts` (D5): a fronteira dele é
786
+ * a conta do sistema, como a do diálogo nativo.
787
+ */
788
+ function provarRespostaCurta(prova, quando) {
789
+ if (!objeto(prova))
790
+ throw semProva('a resposta curta precisa de prova');
791
+ const pergunta = validarRespostaAPergunta(prova.pergunta);
792
+ if (prova.via === 'pulse') {
793
+ const e = prova.envelope;
794
+ if (!objeto(e) || e.origem !== 'telegram')
795
+ throw semProva('a resposta curta pelo canal precisa do envelope assinado do Telegram');
796
+ // GO-FIX 1 (achado 4): o endereço assinado é o do pulse ou o do reply à MESMA pergunta, `resposta:<código dela>:<id>`;
797
+ // o recibo nunca diz uma pergunta e autoriza outra.
798
+ const ref = typeof prova.endereco === 'string' && prova.endereco.startsWith(`${pulse_consentimento_1.ENDERECO_DA_RESPOSTA}:`)
799
+ ? (0, pulse_consentimento_1.lerReferenciaDoReply)(prova.endereco.slice(pulse_consentimento_1.ENDERECO_DA_RESPOSTA.length + 1)) : null;
800
+ if (prova.endereco !== pulse_consentimento_1.ENDERECO_DA_RESPOSTA && ref?.codigo !== pergunta.codigo) {
801
+ throw semProva('o endereço assinado não é o do pulse nem o do reply a esta pergunta');
802
+ }
803
+ try {
804
+ (0, hitl_gates_1.autenticarResposta)(pulse_consentimento_1.ALVO_DO_PULSE, prova.endereco, e, e.recebidoEm);
805
+ (0, hitl_gates_1.autenticarResposta)(pulse_consentimento_1.ALVO_DO_PULSE, prova.endereco, e, quando);
806
+ }
807
+ catch (x) {
808
+ throw semProva(`ingresso não autenticado (${x.message})`);
809
+ }
810
+ if (typeof e.resposta !== 'string' || sha(e.resposta) !== pergunta.respostaSha256) {
811
+ throw semProva('o texto assinado não é a resposta que o núcleo leu');
812
+ }
813
+ return { origem: 'telegram', canal: e.canal ?? null, conta: e.conta ?? null, por: e.por, mensagem: e.mensagem,
814
+ recebidoEm: e.recebidoEm, recibo: sha(e.prova), pergunta: { ...pergunta, enderecoAssinado: { alvo: pulse_consentimento_1.ALVO_DO_PULSE, endereco: prova.endereco } } };
815
+ }
816
+ if (prova.via === 'terminal') {
817
+ const a = prova.atestado;
818
+ const idade = objeto(a) ? Date.parse(quando) - Date.parse(String(a.recebidoEm)) : NaN;
819
+ if (!objeto(a) || a.host !== 'claude-code' || typeof a.sessao !== 'string' || !/^[a-zA-Z0-9][a-zA-Z0-9_-]{0,127}$/.test(a.sessao) ||
820
+ (a.entrypoint !== null && (typeof a.entrypoint !== 'string' || a.entrypoint.length > 64)) || !Array.isArray(a.conferencias) ||
821
+ !a.conferencias.length || a.conferencias.some(c => typeof c !== 'string' || c.length > 80) ||
822
+ !Number.isFinite(idade) || idade < 0 || idade > 60000) {
823
+ throw semProva('o atestado do terminal chegou malformado ou fora da janela de 60 s');
824
+ }
825
+ return { origem: 'terminal', host: 'claude-code', sessao: a.sessao, entrypoint: a.entrypoint, conferencias: [...a.conferencias],
826
+ recebidoEm: a.recebidoEm, solicitacao: (0, node_crypto_1.randomUUID)(), por: 'terminal:claude-code', pergunta };
827
+ }
828
+ throw semProva('a resposta curta chegou por uma via desconhecida');
829
+ }
830
+ /** A subchave do recibo do terminal. */
831
+ function chaveDoTerminal(raiz, criar) {
832
+ return (0, node_crypto_1.createHmac)('sha256', (0, hitl_local_receipt_1.chaveLocal)(raiz, criar)).update(DOMINIO_DO_RECIBO_DO_TERMINAL).digest();
833
+ }
834
+ const nomeDoReciboDoTerminal = (i) => `${sha(`terminal:${i.solicitacao}`)}.json`;
835
+ function dadosDoReciboDoTerminal(arco, ato, envelopeSha256, versao, i) {
836
+ return { contrato: 'ork.noctum-ingresso/v1', arco, endereco: (0, exports.enderecoDoArco)(arco), ato, envelopeSha256, versao, origem: 'terminal',
837
+ host: i.host, sessao: i.sessao, entrypoint: i.entrypoint, conferencias: i.conferencias, solicitacao: i.solicitacao, por: i.por,
838
+ recebidoEm: i.recebidoEm, pergunta: i.pergunta };
839
+ }
840
+ /** O recibo do terminal, com a subchave local: a conferência antes do efeito o relê, como relê o do canal. */
841
+ function gravarReciboDoTerminal(raiz, arco, ato, envelopeSha256, versao, i) {
842
+ const dir = path.join(dirDoArco(raiz, arco), 'ingresso');
843
+ fs.mkdirSync(dir, { recursive: true, mode: 0o700 });
844
+ const nome = nomeDoReciboDoTerminal(i), file = path.join(dir, nome);
845
+ const dados = dadosDoReciboDoTerminal(arco, ato, envelopeSha256, versao, i);
846
+ const bytes = JSON.stringify({ dados, assinatura: (0, node_crypto_1.createHmac)('sha256', chaveDoTerminal(raiz, true)).update(JSON.stringify(dados)).digest('hex') });
847
+ const temp = path.join(dir, `${(0, node_crypto_1.randomUUID)()}.tmp`);
848
+ const fd = fs.openSync(temp, 'wx', 0o600);
849
+ try {
850
+ fs.writeFileSync(fd, bytes);
851
+ fs.fsyncSync(fd);
852
+ }
853
+ finally {
854
+ fs.closeSync(fd);
855
+ }
856
+ try {
857
+ fs.linkSync(temp, file);
858
+ }
859
+ catch (e) {
860
+ if (e.code !== 'EEXIST')
861
+ throw e;
862
+ }
863
+ finally {
864
+ fs.unlinkSync(temp);
865
+ }
866
+ if (lerPrivado(file).toString('utf8') !== bytes)
867
+ throw adulterado('recibo do terminal já existe com outro conteúdo');
868
+ return { evidencia: `.orkastery/noctum/arcos/${arco}/ingresso/${nome}`, evidenciaSha256: sha(bytes) };
869
+ }
870
+ /** Reconfere o recibo do terminal: o arquivo privado, o hash, os dados iguais aos da linha e o MAC da subchave local. */
871
+ function reciboDoTerminalConfere(raiz, arco, ato, envelopeSha256, versao, i) {
872
+ try {
873
+ const nome = nomeDoReciboDoTerminal(i);
874
+ if (i.evidencia !== `.orkastery/noctum/arcos/${arco}/ingresso/${nome}`)
875
+ return 'falhou';
876
+ const bytes = lerPrivado(path.join(dirDoArco(raiz, arco), 'ingresso', nome));
877
+ if (sha(bytes) !== i.evidenciaSha256)
878
+ return 'falhou';
879
+ const v = JSON.parse(bytes.toString('utf8'));
880
+ const { evidencia: _evidencia, evidenciaSha256: _sha, semRecibo: _sem, ...atestado } = i;
881
+ if (!objeto(v) || Object.keys(v).sort().join(',') !== 'assinatura,dados' || typeof v.assinatura !== 'string' ||
882
+ !/^[a-f0-9]{64}$/.test(v.assinatura) || (0, noctum_envelope_1.canonico)(v.dados) !== (0, noctum_envelope_1.canonico)(dadosDoReciboDoTerminal(arco, ato, envelopeSha256, versao, atestado))) {
883
+ return 'falhou';
884
+ }
885
+ return (0, node_crypto_1.timingSafeEqual)(Buffer.from(v.assinatura, 'hex'), (0, node_crypto_1.createHmac)('sha256', chaveDoTerminal(raiz, false)).update(JSON.stringify(v.dados)).digest())
886
+ ? 'ok' : 'falhou';
887
+ }
888
+ catch {
889
+ return 'falhou';
890
+ }
891
+ }
892
+ /** GO-FIX 1 (achado 6 da ork-rm057respost): a resposta não é de antes da pergunta; a mensagem velha não responde à nova. */
893
+ function exigirDepoisDaPergunta(provado, criadaEm) {
894
+ if (!(Date.parse(provado.recebidoEm) >= Date.parse(criadaEm)))
895
+ throw semProva('a mensagem é de antes da pergunta que ela diz responder');
896
+ }
897
+ /**
898
+ * GO-FIX 1 (achado 6 da ork-rm057respost): uma mensagem assinada do dono responde a UMA pergunta. A conferência de "uma
899
+ * prova, um ato" do log vale por arco; esta vale para todos: a mensagem é reivindicada num arquivo privado por mensagem,
900
+ * criado de uma vez (link exclusivo) antes do ato. A mesma mensagem de novo, para a mesma pergunta e o mesmo ato, segue ao
901
+ * caminho de sempre (repetida); para outra pergunta, de qualquer arco, é recusada antes de gravar, mesmo dentro dos 60 s
902
+ * da assinatura (a reentrega do gateway depois de reiniciar, ou um processo que reapresente o envelope).
903
+ */
904
+ function reivindicarMensagem(raiz, chave, alvo, quando) {
905
+ const principal = (0, estado_thread_1.raizDoEstado)(raiz);
906
+ (0, estado_thread_1.exigirEstadoSemLink)(principal, ['.orkastery', 'noctum', 'mensagens-usadas']);
907
+ const dir = path.join(principal, '.orkastery', 'noctum', 'mensagens-usadas');
908
+ fs.mkdirSync(dir, { recursive: true, mode: 0o700 });
909
+ const file = path.join(dir, `${sha(chave)}.json`);
910
+ const dados = JSON.stringify({ contrato: 'ork.noctum-mensagem-usada/v1', mensagemSha256: sha(chave), ...alvo, em: quando });
911
+ const temp = path.join(dir, `${(0, node_crypto_1.randomUUID)()}.tmp`);
912
+ const fd = fs.openSync(temp, 'wx', 0o600);
913
+ try {
914
+ fs.writeFileSync(fd, dados);
915
+ fs.fsyncSync(fd);
916
+ }
917
+ finally {
918
+ fs.closeSync(fd);
919
+ }
920
+ try {
921
+ fs.linkSync(temp, file);
922
+ return;
923
+ }
924
+ catch (e) {
925
+ if (e.code !== 'EEXIST')
926
+ throw e;
927
+ }
928
+ finally {
929
+ fs.unlinkSync(temp);
930
+ }
931
+ let antes;
932
+ try {
933
+ antes = JSON.parse(lerPrivado(file).toString('utf8'));
934
+ }
935
+ catch {
936
+ throw semProva('o registro desta mensagem não se lê; nada foi gravado');
937
+ }
938
+ if (!objeto(antes) || antes.arco !== alvo.arco || antes.codigo !== alvo.codigo || antes.ato !== alvo.ato) {
939
+ throw semProva('esta mensagem já respondeu a outra pergunta; mande outra');
940
+ }
941
+ }
942
+ /** O ingresso do dono com o recibo durável da via (o do canal ou o do terminal). */
943
+ function comRecibo(raiz, arco, ato, envelopeSha256, versao, provado) {
944
+ return provado.origem === 'terminal'
945
+ ? { ...provado, ...gravarReciboDoTerminal(raiz, arco, ato, envelopeSha256, versao, provado) }
946
+ : { ...provado, ...gravarRecibo(raiz, arco, ato, envelopeSha256, versao, provado) };
947
+ }
948
+ /**
949
+ * RM-057 (resposta curta): autoriza a proposta pela resposta curta do dono à pergunta dela. O hash vem da pergunta (a
950
+ * proposta com este código), nunca da resposta. Sob o lock do arco: a pergunta tem de ser a de agora (a proposta mais
951
+ * nova, no prazo e na janela), a prova não pode ter servido a outro ato, e o ato só volta com sucesso relido do disco.
952
+ */
953
+ function autorizarPelaResposta(raiz, arco, codigo, prova, quando = new Date().toISOString(), info) {
954
+ return comLockDoArco(raiz, arco, false, () => {
955
+ const log = lerLog(raiz, arco), d = derivar(log.linhas);
956
+ const proposta = [...d.propostas.values()].find(p => p.codigo === codigo);
957
+ if (!proposta)
958
+ throw new noctum_envelope_1.RecusaDoArco('noctum.pergunta.desconhecida', `o arco ${arco} não tem a pergunta ${codigo}`);
959
+ const provado = provarRespostaCurta(prova, quando);
960
+ if (provado.pergunta?.codigo !== codigo)
961
+ throw semProva('a prova responde a outra pergunta');
962
+ exigirDepoisDaPergunta(provado, proposta.em);
963
+ if (provado.origem === 'telegram')
964
+ reivindicarMensagem(raiz, chaveDaProva(provado), { arco, codigo, ato: 'autorizar' }, quando);
965
+ if (d.autorizacao?.envelopeSha256 === proposta.envelopeSha256 && d.autorizacao.versao === proposta.versao && !d.revogacao) {
966
+ const atual = estadoDe(arco, d, quando);
967
+ if (atual.situacao === 'encerrado') {
968
+ throw new noctum_envelope_1.RecusaDoArco('noctum.efeito.encerrado', `arco ${arco} encerrado (${d.encerramento?.motivo ?? 'fechado'}) é terminal; abra outro arco`);
969
+ }
970
+ if (atual.situacao === 'expirado') {
971
+ throw new noctum_envelope_1.RecusaDoArco('noctum.efeito.expirado', `o arco ${arco} terminou em ${atual.conteudo?.janela.fim ?? 'o fim da janela'}; abra outro arco`);
972
+ }
973
+ if (info) {
974
+ info.repetida = true;
975
+ info.mesmaProva = chaveDaProva(d.autorizacao.ingresso) === chaveDaProva(provado);
976
+ }
977
+ return atual;
978
+ }
979
+ if (d.usadas.has(chaveDaProva(provado)))
980
+ throw semProva('esta mensagem já provou outro ato neste arco');
981
+ // A pergunta respondida é a de agora: a proposta mais nova, no prazo e na janela (a recusa diz por que não).
982
+ const autorizavel = propostaAutorizavel(d, proposta.envelopeSha256, quando);
983
+ const ingresso = comRecibo(raiz, arco, 'autorizar', autorizavel.envelopeSha256, autorizavel.versao, provado);
984
+ anexar(raiz, arco, log, { tipo: 'autorizado', versao: autorizavel.versao, envelopeSha256: autorizavel.envelopeSha256, ingresso }, quando);
985
+ return confirmado(raiz, arco, quando, e => e.envelopeSha256 === autorizavel.envelopeSha256 && e.situacao !== 'revogado', 'autorização');
986
+ });
987
+ }
988
+ /**
989
+ * RM-057 (resposta curta, D10): revoga o arco pela resposta curta do dono à pergunta de revogação. Terminal, como a
990
+ * revogação assinada. A pergunta tem de ser a última de revogação, no prazo; o recibo é auditoria e nunca segura o ato.
991
+ */
992
+ function revogarPelaResposta(raiz, arco, codigo, prova, quando = new Date().toISOString(), info) {
993
+ return comLockDoArco(raiz, arco, false, () => {
994
+ const log = lerLog(raiz, arco), d = derivar(log.linhas);
995
+ const pedida = d.revogacoesPedidas.find(r => r.codigo === codigo);
996
+ if (!pedida)
997
+ throw new noctum_envelope_1.RecusaDoArco('noctum.pergunta.desconhecida', `o arco ${arco} não tem a pergunta ${codigo}`);
998
+ const provado = provarRespostaCurta(prova, quando);
999
+ if (provado.pergunta?.codigo !== codigo)
1000
+ throw semProva('a prova responde a outra pergunta');
1001
+ exigirDepoisDaPergunta(provado, pedida.em);
1002
+ if (provado.origem === 'telegram')
1003
+ reivindicarMensagem(raiz, chaveDaProva(provado), { arco, codigo, ato: 'revogar' }, quando);
1004
+ if (d.revogacao) {
1005
+ if (info) {
1006
+ info.repetida = true;
1007
+ info.mesmaProva = chaveDaProva(d.revogacao) === chaveDaProva(provado);
1008
+ }
1009
+ return estadoDe(arco, d, quando);
1010
+ }
1011
+ if (d.revogacoesPedidas.at(-1)?.codigo !== codigo) {
1012
+ throw new noctum_envelope_1.RecusaDoArco('noctum.pergunta.substituida', `a pergunta ${codigo} foi substituída por outra de revogação`);
1013
+ }
1014
+ if (!(Date.parse(quando) < Date.parse(pedida.prazo))) {
1015
+ throw new noctum_envelope_1.RecusaDoArco('noctum.pergunta.vencida', `a pergunta ${codigo} venceu em ${(0, horario_1.formatarDataHora)(pedida.prazo)}; peça a revogação de novo`);
1016
+ }
1017
+ if (d.usadas.has(chaveDaProva(provado)))
1018
+ throw semProva('esta mensagem já provou outro ato neste arco');
1019
+ let recibo;
1020
+ try {
1021
+ const comProva = comRecibo(raiz, arco, 'revogar', null, null, provado);
1022
+ recibo = { evidencia: comProva.evidencia, evidenciaSha256: comProva.evidenciaSha256 };
1023
+ }
1024
+ catch (e) {
1025
+ recibo = { evidencia: '', evidenciaSha256: '', semRecibo: e instanceof noctum_envelope_1.RecusaDoArco ? e.motivo : 'noctum.recibo.nao-gravado' };
1026
+ }
1027
+ anexar(raiz, arco, log, { tipo: 'revogado', ingresso: { ...provado, ...recibo } }, quando);
1028
+ return confirmado(raiz, arco, quando, e => e.situacao === 'revogado', 'revogação');
1029
+ }, true);
1030
+ }
1031
+ /** As opções do diálogo, uma por alternativa da pergunta, com o selo na recomendada. */
1032
+ function opcoesDoDialogo(ato) {
1033
+ return (0, noctum_pergunta_1.alternativasDaPergunta)({ ato }).map(a => `${a.letra}) ${a.texto}${a.porque ? ' (Recomendação)' : ''}`);
1034
+ }
604
1035
  /**
605
1036
  * O diálogo nativo do host para autorizar ou revogar, com o callback de elicitação fornecido pelo
606
1037
  * servidor, nunca pelos argumentos de uma ferramenta. É a mesma fronteira de `criarIngressoLocal`:
@@ -621,6 +1052,7 @@ function criarDialogoDoArco(raiz, origem, elicitar) {
621
1052
  try {
622
1053
  const cancelar = new Promise(r => controller.signal.addEventListener('abort', () => r({ action: 'cancel' }), { once: true }));
623
1054
  const r = await Promise.race([elicitar(JSON.parse(JSON.stringify(a)), controller.signal), cancelar]);
1055
+ // Só a primeira alternativa (a recomendada) age; adiar e mudar algo não fazem nada (D13 da ork-rm057respost).
624
1056
  if (!objeto(r) || r.action !== 'accept' || !objeto(r.content) || Object.keys(r.content).length !== 1 ||
625
1057
  r.content.opcao !== a.opcoes[0])
626
1058
  throw semProva('o dono não confirmou pelo diálogo nativo do host');
@@ -641,8 +1073,11 @@ function criarDialogoDoArco(raiz, origem, elicitar) {
641
1073
  const d = derivar(lerLog(raiz, arco).linhas);
642
1074
  return propostaAutorizavel(d, envelopeSha256, new Date().toISOString());
643
1075
  });
644
- const a = { contrato: 'ork.noctum-dialogo/v1', arco, ato: 'autorizar', envelopeSha256,
645
- resumo: (0, noctum_envelope_1.resumoDoEnvelope)(antes.conteudo, envelopeSha256), opcoes: ['Autorizar este envelope', 'Não autorizar'] };
1076
+ const resumo = (0, noctum_envelope_1.resumoDoEnvelope)(antes.conteudo, envelopeSha256), quando = new Date().toISOString();
1077
+ const pergunta = { contrato: noctum_pergunta_1.CONTRATO_PERGUNTA_DO_ARCO, arco, ato: 'autorizar', codigo: antes.codigo ?? '',
1078
+ versao: antes.versao, envelopeSha256, criadaEm: antes.em, prazo: antes.prazo, conteudo: antes.conteudo };
1079
+ const a = { contrato: 'ork.noctum-dialogo/v1', arco, ato: 'autorizar', envelopeSha256, resumo,
1080
+ mensagem: [(0, noctum_pergunta_1.textoDoDialogoDoArco)(pergunta, quando), '', ...resumo].join('\n'), opcoes: opcoesDoDialogo('autorizar') };
646
1081
  const { requestId, recebidoEm } = await perguntar(a, antes.prazo);
647
1082
  // O mundo pode ter mudado enquanto o dono lia: tudo é conferido de novo sob o lock.
648
1083
  return comLockDoArcoAssincrono(raiz, arco, () => {
@@ -660,9 +1095,13 @@ function criarDialogoDoArco(raiz, origem, elicitar) {
660
1095
  const atual = lerArco(raiz, arco);
661
1096
  if (!atual)
662
1097
  throw new noctum_envelope_1.RecusaDoArco('noctum.efeito.sem-autorizacao', `arco ${arco} sem proposta`);
663
- const a = { contrato: 'ork.noctum-dialogo/v1', arco, ato: 'revogar', envelopeSha256: atual.envelopeSha256,
664
- resumo: [`Revogar o arco ${arco}: nenhum efeito novo passa depois disto, e o arco não reabre`],
665
- opcoes: ['Revogar o arco', 'Manter o arco'] };
1098
+ const resumo = [`Revogar o arco ${arco}: nenhum efeito novo passa depois disto, e o arco não reabre`];
1099
+ const quando = new Date().toISOString();
1100
+ const pergunta = { contrato: noctum_pergunta_1.CONTRATO_PERGUNTA_DO_ARCO, arco, ato: 'revogar', codigo: atual.revogacaoPedida?.codigo ?? '',
1101
+ versao: atual.versao, envelopeSha256: atual.envelopeSha256, criadaEm: quando,
1102
+ prazo: atual.revogacaoPedida?.prazo ?? new Date(Date.parse(quando) + ESPERA_DO_DIALOGO_MS).toISOString(), conteudo: atual.conteudo };
1103
+ const a = { contrato: 'ork.noctum-dialogo/v1', arco, ato: 'revogar', envelopeSha256: atual.envelopeSha256, resumo,
1104
+ mensagem: [(0, noctum_pergunta_1.textoDoDialogoDoArco)(pergunta, quando), '', ...resumo].join('\n'), opcoes: opcoesDoDialogo('revogar') };
666
1105
  const { requestId, recebidoEm } = await perguntar(a, undefined);
667
1106
  return comLockDoArcoAssincrono(raiz, arco, () => {
668
1107
  const log = lerLog(raiz, arco), d = derivar(log.linhas);
@@ -745,6 +1184,17 @@ function lerArco(raiz, arco, quando = new Date().toISOString()) {
745
1184
  const log = lerLog(raiz, arco);
746
1185
  return log.linhas.length ? estadoDe(arco, derivar(log.linhas), quando) : null;
747
1186
  }
1187
+ /**
1188
+ * RM-058, integração (D3 da ork-rm058integra): a guarda das threads do arco (`noctum-guarda.ts`) lê a lista de trabalhos
1189
+ * do envelope vigente e a da proposta no prazo; o estado só traz o hash e o prazo dela. `null` quando o arco não existe.
1190
+ */
1191
+ function lerArcoComProposta(raiz, arco, quando = new Date().toISOString()) {
1192
+ const log = lerLog(raiz, arco);
1193
+ if (!log.linhas.length)
1194
+ return null;
1195
+ const d = derivar(log.linhas), estado = estadoDe(arco, d, quando);
1196
+ return { estado, proposta: estado.propostaPendente ? d.propostas.get(estado.propostaPendente.versao).conteudo : null };
1197
+ }
748
1198
  /**
749
1199
  * A conferência imediatamente antes de um efeito. Relê o disco, sem cache, e recusa na ordem:
750
1200
  * ação excluída, estado adulterado (inclusive cauda incompleta), revogação, falta de autorização,
@@ -775,6 +1225,11 @@ function vigenteConferido(raiz, arco, envelopeSha256, log) {
775
1225
  throw new noctum_envelope_1.RecusaDoArco('noctum.efeito.revogado', `arco ${arco} revogado pelo dono`);
776
1226
  if (!d.autorizacao)
777
1227
  throw new noctum_envelope_1.RecusaDoArco('noctum.efeito.sem-autorizacao', `arco ${arco} sem envelope autorizado`);
1228
+ // RM-058 N6 (D2): encerrado é terminal como o revogado; reserva devolvida depois do teto não o reabre.
1229
+ if (d.encerramento) {
1230
+ throw new noctum_envelope_1.RecusaDoArco('noctum.efeito.encerrado', `arco ${arco} encerrado (${d.encerramento.motivo}) em ${(0, horario_1.formatarDataHora)(d.encerramento.em)}; ` +
1231
+ 'nenhum despacho ou efeito novo passa, e o arco não reabre');
1232
+ }
778
1233
  // A prova do dono é reconferida aqui, pelo recibo do canal, e não lida da linha do log (FX2 no gate).
779
1234
  const prova = reciboConfere(raiz, arco, 'autorizar', d.autorizacao.envelopeSha256, d.autorizacao.versao, d.autorizacao.ingresso);
780
1235
  if (prova === 'sem-verificador') {
@@ -824,8 +1279,9 @@ function threadsDoTrabalho(raiz, item, texto) {
824
1279
  /**
825
1280
  * D11 e D12 da ork-rm058n2: confere o envelope e reserva o despacho, as duas coisas sob o lock do arco, imediatamente
826
1281
  * antes do efeito. A cadeia da N1 (`vigenteConferido`) mais a janela, o trabalho na lista `v2`, a conta e o runtime do
827
- * envelope e o orçamento: cada despacho reserva uma sessão no teto do arco e nos subtetos da conta e do runtime, e teto
828
- * em outra unidade recusa, porque a N2 não mede consumo nela. O trabalho de pedido só despacha a thread que o arco abriu
1282
+ * envelope e o orçamento: cada despacho reserva uma sessão no teto do arco e nos subtetos da conta e do runtime; teto em
1283
+ * outra unidade confere o consumo medido que o passo traz (`pedido.medido`, D5 da ork-rm058n6) e, sem ele ou com lacuna,
1284
+ * recusa (`sem-medicao`). O trabalho de pedido só despacha a thread que o arco abriu
829
1285
  * para ele, e só abre uma pelo GOAL quando nenhuma casa ainda; SHIP e MASTER nunca saem do arco da N2. A revogação usa o
830
1286
  * mesmo lock: ou ela vem antes e nada é reservado, ou vem depois e vale para o despacho seguinte.
831
1287
  */
@@ -869,13 +1325,24 @@ function reservarDespacho(raiz, pedido, quando = new Date().toISOString()) {
869
1325
  throw fora(`a conta ${String(pedido.conta)} não está no envelope`);
870
1326
  }
871
1327
  const consumo = consumoDe(d);
872
- const tetos = [['do arco', c.orcamento.arco, consumo.arco],
873
- [`da conta ${pedido.conta}`, tetoDaConta, consumo.porConta[pedido.conta] ?? 0],
874
- [`do runtime ${pedido.runtime}`, tetoDoRuntime, consumo.porRuntime[pedido.runtime] ?? 0]];
875
- for (const [nome, teto, usado] of tetos) {
1328
+ const tetos = [['do arco', c.orcamento.arco, consumo.arco, 'arco'],
1329
+ [`da conta ${pedido.conta}`, tetoDaConta, consumo.porConta[pedido.conta] ?? 0, 'conta'],
1330
+ [`do runtime ${pedido.runtime}`, tetoDoRuntime, consumo.porRuntime[pedido.runtime] ?? 0, 'runtime']];
1331
+ for (const [nome, teto, usado, chave] of tetos) {
876
1332
  if (teto.unidade !== 'sessoes') {
877
- throw new noctum_envelope_1.RecusaDoArco('noctum.orcamento.sem-medicao', `o teto ${nome} está em ${teto.unidade}, e a N2 só mede sessoes: ` +
878
- 'sem medir o consumo, o teto não é garantido, e consumo desconhecido nunca vale zero');
1333
+ // RM-058 N6 (D5): fora de `sessoes`, o consumo medido fora do lock decide; sem medida, ou com lacuna, recusa.
1334
+ const medido = pedido.medido?.[chave];
1335
+ if (!medido || medido.unidade !== teto.unidade || 'lacuna' in medido || !Number.isFinite(medido.valor)) {
1336
+ throw new noctum_envelope_1.RecusaDoArco('noctum.orcamento.sem-medicao', `o teto ${nome} está em ${teto.unidade}, e o consumo nessa unidade não foi ` +
1337
+ `medido para esta reserva${medido && 'lacuna' in medido ? ` (${medido.lacuna})` : ''}: sem medir, o teto não é garantido, e ` +
1338
+ 'consumo desconhecido nunca vale zero');
1339
+ }
1340
+ if (medido.valor >= teto.valor) {
1341
+ const valor = teto.unidade === 'percentual-da-cota' ? `${Math.round(medido.valor * 10) / 10}%` : String(medido.valor);
1342
+ throw new noctum_envelope_1.RecusaDoArco('noctum.orcamento.esgotado', `o teto ${nome} é de ${teto.valor} ${teto.unidade} e o consumo medido é ` +
1343
+ (medido.tetoSuperior ? `até ${valor} (teto superior: inclui uso de fora do arco)` : valor));
1344
+ }
1345
+ continue;
879
1346
  }
880
1347
  if (usado + 1 > teto.valor) {
881
1348
  throw new noctum_envelope_1.RecusaDoArco('noctum.orcamento.esgotado', `o teto ${nome} é de ${teto.valor} sessoes e ${usado} já foram reservadas`);
@@ -889,6 +1356,26 @@ function reservarDespacho(raiz, pedido, quando = new Date().toISOString()) {
889
1356
  return { arco: pedido.arco, seq, versao: vigente.versao, envelopeSha256: vigente.envelopeSha256, fim: c.janela.fim, consumo: estado.consumo };
890
1357
  }, true);
891
1358
  }
1359
+ /**
1360
+ * RM-058 N6 (D2 e D8 da ork-rm058n6): fecha o arco, uma vez, sob o lock do arco (espera como a revogação, porque é ato de
1361
+ * segurança). Depois disto `vigenteConferido` recusa todo despacho e efeito com `noctum.efeito.encerrado`, a emenda é
1362
+ * recusada e a fila encerra. O segundo fechamento devolve o primeiro. Só fecha arco que o dono autorizou; o revogado
1363
+ * também fecha, para o registro do fechamento (a situação continua `revogado`).
1364
+ */
1365
+ function encerrarArco(raiz, arco, fechamento, quando = new Date().toISOString()) {
1366
+ if (!exports.MOTIVOS_DE_ENCERRAMENTO.includes(fechamento?.motivo)) {
1367
+ throw new noctum_envelope_1.RecusaDoArco('noctum.envelope.invalido', `motivo de encerramento fora de ${exports.MOTIVOS_DE_ENCERRAMENTO.join(', ')}`);
1368
+ }
1369
+ return comLockDoArco(raiz, arco, false, () => {
1370
+ const log = lerLog(raiz, arco), d = derivar(log.linhas);
1371
+ if (!d.autorizacao)
1372
+ throw new noctum_envelope_1.RecusaDoArco('noctum.efeito.sem-autorizacao', `arco ${arco} nunca autorizado não encerra`);
1373
+ if (d.encerramento)
1374
+ return estadoDe(arco, d, quando);
1375
+ anexar(raiz, arco, log, { tipo: 'encerrado', motivo: fechamento.motivo, detalhe: String(fechamento.detalhe ?? '').slice(0, 500) }, quando);
1376
+ return confirmado(raiz, arco, quando, e => e.encerramento !== null, 'encerramento');
1377
+ }, true);
1378
+ }
892
1379
  /**
893
1380
  * O desfecho da reserva, uma vez só: o despacho que abriu sessão (ou que a sessão em andamento já atendia), ou o que o
894
1381
  * núcleo recusou, que devolve a sessão ao orçamento. Vale também depois de revogação ou expiração: é a contabilidade do
@@ -909,3 +1396,23 @@ function concluirDespacho(raiz, arco, seq, desfecho, quando = new Date().toISOSt
909
1396
  return confirmado(raiz, arco, quando, e => e.despachos.some(x => x.seq === seq && x.desfecho !== 'pendente'), 'conclusão do despacho');
910
1397
  }, true);
911
1398
  }
1399
+ /**
1400
+ * RM-058 N3 (D6 da ork-rm058n3): as versões do envelope que o dono autorizou neste arco, na ordem do log. O ramo de
1401
+ * coordenação guarda registros assinados sob versões anteriores (a vaga tomada antes de uma emenda continua contando), e a
1402
+ * leitura confere cada um com as máquinas da versão que ele diz. A vigente é a última; o recibo do dono continua conferido
1403
+ * por `vigenteConferido`, antes de cada efeito.
1404
+ */
1405
+ function versoesAutorizadas(raiz, arco) {
1406
+ const log = lerLog(raiz, arco), d = derivar(log.linhas);
1407
+ const vistas = new Set();
1408
+ const versoes = [];
1409
+ for (const { evento: e } of log.linhas) {
1410
+ if (e.tipo !== 'autorizado' || vistas.has(e.versao))
1411
+ continue;
1412
+ vistas.add(e.versao);
1413
+ const p = d.propostas.get(e.versao);
1414
+ // RM-058, integração (T8, D12): o instante do ingresso de cada autorização, que o aceite do piloto lê.
1415
+ versoes.push({ versao: p.versao, envelopeSha256: p.envelopeSha256, conteudo: p.conteudo, autorizadoEm: e.ingresso.recebidoEm });
1416
+ }
1417
+ return versoes;
1418
+ }