@orkastery/cli 0.5.3 → 0.6.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.
- package/adapters/README.md +18 -0
- package/adapters/claude-code/.claude-plugin/plugin.json +1 -0
- package/adapters/claude-code/README.md +195 -5
- package/adapters/claude-code/agents/ork-check.md +1 -0
- package/adapters/claude-code/agents/ork-go.md +1 -0
- package/adapters/claude-code/agents/ork-goal.md +1 -0
- package/adapters/claude-code/agents/ork-master.md +1 -0
- package/adapters/claude-code/agents/ork-plan.md +1 -0
- package/adapters/claude-code/agents/ork-ship.md +1 -0
- package/adapters/claude-code/hooks/hooks.json +2 -1
- package/adapters/claude-code/hooks/ork-guard.js +15 -1
- package/adapters/claude-code/hooks/ork-resposta.js +91 -0
- package/adapters/claude-code/hooks/ork-sensor.js +18 -2
- package/adapters/claude-code/hooks/quota-signal.js +14 -0
- package/adapters/claude-code/mods/autoridade.json +56 -0
- package/adapters/claude-code/mods/ork-clima/.claude-plugin/plugin.json +14 -0
- package/adapters/claude-code/mods/ork-clima/hooks/contas.ts +283 -0
- package/adapters/claude-code/mods/ork-clima/hooks/hooks.json +3 -0
- package/adapters/claude-code/mods/ork-clima/hooks/previsao.ts +393 -0
- package/adapters/claude-code/mods/ork-clima/hooks/register.ts +205 -0
- package/adapters/claude-code/mods/ork-clima/orkastery-mod.json +5 -0
- package/adapters/claude-code/mods/ork-clima/types/index.d.ts +80 -0
- package/adapters/claude-code/mods/ork-freio/.claude-plugin/plugin.json +14 -0
- package/adapters/claude-code/mods/ork-freio/hooks/carga.ts +160 -0
- package/adapters/claude-code/mods/ork-freio/hooks/conferencia.ts +309 -0
- package/adapters/claude-code/mods/ork-freio/hooks/hooks.json +3 -0
- package/adapters/claude-code/mods/ork-freio/hooks/lote.ts +270 -0
- package/adapters/claude-code/mods/ork-freio/hooks/register.ts +285 -0
- package/adapters/claude-code/mods/ork-freio/orkastery-mod.json +5 -0
- package/adapters/claude-code/mods/ork-freio/types/index.d.ts +127 -0
- package/adapters/claude-code/mods/ork-mensageiro/.claude-plugin/plugin.json +14 -0
- package/adapters/claude-code/mods/ork-mensageiro/hooks/caixa.ts +215 -0
- package/adapters/claude-code/mods/ork-mensageiro/hooks/hooks.json +3 -0
- package/adapters/claude-code/mods/ork-mensageiro/hooks/painel.tsx +165 -0
- package/adapters/claude-code/mods/ork-mensageiro/hooks/recepcao.ts +256 -0
- package/adapters/claude-code/mods/ork-mensageiro/hooks/register.tsx +481 -0
- package/adapters/claude-code/mods/ork-mensageiro/orkastery-mod.json +5 -0
- package/adapters/claude-code/mods/ork-mensageiro/types/index.d.ts +85 -0
- package/adapters/claude-code/mods/ork-mesa/.claude-plugin/plugin.json +14 -0
- package/adapters/claude-code/mods/ork-mesa/hooks/cartoes.tsx +246 -0
- package/adapters/claude-code/mods/ork-mesa/hooks/decidir.ts +110 -0
- package/adapters/claude-code/mods/ork-mesa/hooks/fila.ts +253 -0
- package/adapters/claude-code/mods/ork-mesa/hooks/hooks.json +3 -0
- package/adapters/claude-code/mods/ork-mesa/hooks/obvia.ts +98 -0
- package/adapters/claude-code/mods/ork-mesa/hooks/register.tsx +493 -0
- package/adapters/claude-code/mods/ork-mesa/orkastery-mod.json +5 -0
- package/adapters/claude-code/mods/ork-mesa/types/index.d.ts +131 -0
- package/adapters/claude-code/mods/ork-pista/.claude-plugin/plugin.json +14 -0
- package/adapters/claude-code/mods/ork-pista/hooks/acoes.ts +71 -0
- package/adapters/claude-code/mods/ork-pista/hooks/ci.ts +121 -0
- package/adapters/claude-code/mods/ork-pista/hooks/hooks.json +3 -0
- package/adapters/claude-code/mods/ork-pista/hooks/pista.tsx +356 -0
- package/adapters/claude-code/mods/ork-pista/hooks/register.tsx +439 -0
- package/adapters/claude-code/mods/ork-pista/orkastery-mod.json +5 -0
- package/adapters/claude-code/mods/ork-pista/types/index.d.ts +85 -0
- package/adapters/claude-code/mods/ork-ponte/.claude-plugin/plugin.json +13 -0
- package/adapters/claude-code/mods/ork-ponte/hooks/faixa.tsx +57 -0
- package/adapters/claude-code/mods/ork-ponte/hooks/guarda.ts +410 -0
- package/adapters/claude-code/mods/ork-ponte/hooks/hooks.json +3 -0
- package/adapters/claude-code/mods/ork-ponte/hooks/mostrado.ts +55 -0
- package/adapters/claude-code/mods/ork-ponte/hooks/nucleo.ts +157 -0
- package/adapters/claude-code/mods/ork-ponte/hooks/papel.ts +42 -0
- package/adapters/claude-code/mods/ork-ponte/hooks/register.ts +331 -0
- package/adapters/claude-code/mods/ork-ponte/hooks/vigia.ts +76 -0
- package/adapters/claude-code/mods/ork-ponte/orkastery-mod.json +5 -0
- package/adapters/claude-code/mods/ork-ponte/types/index.d.ts +178 -0
- package/adapters/claude-code/mods/ork-portao/.claude-plugin/plugin.json +14 -0
- package/adapters/claude-code/mods/ork-portao/hooks/hooks.json +3 -0
- package/adapters/claude-code/mods/ork-portao/hooks/lease.ts +201 -0
- package/adapters/claude-code/mods/ork-portao/hooks/plano.ts +304 -0
- package/adapters/claude-code/mods/ork-portao/hooks/register.ts +339 -0
- package/adapters/claude-code/mods/ork-portao/hooks/verify.ts +152 -0
- package/adapters/claude-code/mods/ork-portao/orkastery-mod.json +5 -0
- package/adapters/claude-code/mods/ork-portao/types/index.d.ts +60 -0
- package/adapters/claude-code/mods/ork-radar/.claude-plugin/plugin.json +14 -0
- package/adapters/claude-code/mods/ork-radar/hooks/colisao.ts +420 -0
- package/adapters/claude-code/mods/ork-radar/hooks/hooks.json +3 -0
- package/adapters/claude-code/mods/ork-radar/hooks/mapa.tsx +322 -0
- package/adapters/claude-code/mods/ork-radar/hooks/register.tsx +470 -0
- package/adapters/claude-code/mods/ork-radar/orkastery-mod.json +5 -0
- package/adapters/claude-code/mods/ork-radar/types/index.d.ts +72 -0
- package/adapters/claude-code/mods/ork-ritual/.claude-plugin/plugin.json +14 -0
- package/adapters/claude-code/mods/ork-ritual/hooks/hooks.json +3 -0
- package/adapters/claude-code/mods/ork-ritual/hooks/nota.ts +88 -0
- package/adapters/claude-code/mods/ork-ritual/hooks/register.tsx +494 -0
- package/adapters/claude-code/mods/ork-ritual/hooks/replay.tsx +271 -0
- package/adapters/claude-code/mods/ork-ritual/hooks/ritual.ts +218 -0
- package/adapters/claude-code/mods/ork-ritual/orkastery-mod.json +5 -0
- package/adapters/claude-code/mods/ork-ritual/types/index.d.ts +106 -0
- package/adapters/claude-code/mods/ork-sentinela/.claude-plugin/plugin.json +14 -0
- package/adapters/claude-code/mods/ork-sentinela/hooks/alertas.ts +85 -0
- package/adapters/claude-code/mods/ork-sentinela/hooks/despacho.ts +156 -0
- package/adapters/claude-code/mods/ork-sentinela/hooks/hooks.json +3 -0
- package/adapters/claude-code/mods/ork-sentinela/hooks/register.ts +511 -0
- package/adapters/claude-code/mods/ork-sentinela/hooks/vigia.ts +186 -0
- package/adapters/claude-code/mods/ork-sentinela/orkastery-mod.json +5 -0
- package/adapters/claude-code/mods/ork-sentinela/types/index.d.ts +65 -0
- package/adapters/claude-code/mods/ork-torre/.claude-plugin/plugin.json +14 -0
- package/adapters/claude-code/mods/ork-torre/hooks/agenda.ts +60 -0
- package/adapters/claude-code/mods/ork-torre/hooks/detalhe.tsx +236 -0
- package/adapters/claude-code/mods/ork-torre/hooks/frases.ts +42 -0
- package/adapters/claude-code/mods/ork-torre/hooks/hooks.json +3 -0
- package/adapters/claude-code/mods/ork-torre/hooks/painel.tsx +344 -0
- package/adapters/claude-code/mods/ork-torre/hooks/rede.ts +97 -0
- package/adapters/claude-code/mods/ork-torre/hooks/register.tsx +437 -0
- package/adapters/claude-code/mods/ork-torre/orkastery-mod.json +5 -0
- package/adapters/claude-code/mods/ork-torre/types/index.d.ts +108 -0
- package/adapters/codex/skills/ork/SKILL.md +2 -0
- package/adapters/hermes/README.md +64 -1
- package/adapters/hermes/bin/ork-conhecimento-colecoes.sh +14 -0
- package/adapters/hermes/bin/ork-conhecimento-listar.sh +15 -0
- package/adapters/hermes/bin/ork-conhecimento-mostrar.sh +16 -0
- package/adapters/hermes/bin/ork-conhecimento-verificar.sh +15 -0
- package/adapters/hermes/bin/ork-vigia-batida.py +127 -0
- package/adapters/hermes/hermes.plugin.json +4 -0
- package/adapters/hermes/hitl-ingress/__init__.py +101 -8
- package/adapters/hermes/skills/orkastery-devmaster/SKILL.md +3 -3
- package/adapters/openclaw/README.md +83 -10
- package/adapters/openclaw/bin/ork-vigia-batida.js +86 -0
- package/adapters/openclaw/dist/hitl-ingress.js +106 -9
- package/adapters/openclaw/dist/index.js +86 -0
- package/adapters/openclaw/openclaw.plugin.json +34 -4
- package/adapters/openclaw/package.json +2 -2
- package/adapters/openclaw/src/hitl-ingress.ts +103 -9
- package/adapters/openclaw/src/index.ts +94 -0
- package/assets/adapters-en/claude-code/README.md +403 -0
- package/assets/adapters-en/claude-code/agents/ork-check.md +33 -0
- package/assets/adapters-en/claude-code/agents/ork-go.md +33 -0
- package/assets/adapters-en/claude-code/agents/ork-goal.md +34 -0
- package/assets/adapters-en/claude-code/agents/ork-master.md +35 -0
- package/assets/adapters-en/claude-code/agents/ork-plan.md +33 -0
- package/assets/adapters-en/claude-code/agents/ork-ship.md +33 -0
- package/assets/adapters-en/claude-code/commands/check.md +46 -0
- package/assets/adapters-en/claude-code/commands/go.md +46 -0
- package/assets/adapters-en/claude-code/commands/goal.md +55 -0
- package/assets/adapters-en/claude-code/commands/master.md +54 -0
- package/assets/adapters-en/claude-code/commands/onboarding.md +22 -0
- package/assets/adapters-en/claude-code/commands/ork.md +126 -0
- package/assets/adapters-en/claude-code/commands/plan.md +47 -0
- package/assets/adapters-en/claude-code/commands/ship.md +45 -0
- package/assets/adapters-en/codex/skills/ork/SKILL.md +85 -0
- package/assets/adapters-en/hermes/README.md +273 -0
- package/assets/adapters-en/hermes/skills/orkastery-devmaster/SKILL.md +169 -0
- package/assets/docs-en/markdownlint-cli2.jsonc +22 -0
- package/assets/docs-en/padroes/documentacao-de-produto.md +135 -0
- package/assets/docs-en/padroes/roadmap-de-produto.md +100 -0
- package/assets/docs-en/produto/README.md +14 -0
- package/assets/docs-en/produto/_modelo-feature.md +62 -0
- package/assets/docs-en/roadmap/README.md +14 -0
- package/assets/docs-en/roadmap/_modelo-item.md +75 -0
- package/assets/references-en/README.md +25 -0
- package/assets/references-en/code-review-axes.md +82 -0
- package/assets/references-en/definition-of-done.md +74 -0
- package/assets/references-en/performance-checklist.md +53 -0
- package/assets/references-en/security-checklist.md +101 -0
- package/assets/references-en/testing-patterns.md +51 -0
- package/assets/security-scan/defaults/.golangci.yml +2 -0
- package/assets/security-scan/defaults/eslint.config.mjs +11 -0
- package/assets/security-scan/defaults/gitleaks.toml +3 -0
- package/assets/security-scan/defaults/ruff.toml +1 -0
- package/assets/security-scan/tools-agil.json +87 -0
- package/assets/security-scan/tools.json +625 -0
- package/assets/skills-en/core/onboarding/SKILL.md +58 -0
- package/assets/skills-en/core/orkastery-bootstrap/SKILL.md +119 -0
- package/assets/skills-en/core/thread-state/SKILL.md +88 -0
- package/assets/skills-en/governance/decision-triage/SKILL.md +65 -0
- package/assets/skills-en/governance/narrative-guardian/SKILL.md +66 -0
- package/assets/skills-en/governance/roadmap-keeper/SKILL.md +60 -0
- package/assets/skills-en/governance/scope-check-capability-map/SKILL.md +71 -0
- package/assets/skills-en/observability/thread-tracing/SKILL.md +63 -0
- package/assets/skills-en/phases/check-quality/SKILL.md +103 -0
- package/assets/skills-en/phases/go-implementation/SKILL.md +78 -0
- package/assets/skills-en/phases/goal-definition/SKILL.md +69 -0
- package/assets/skills-en/phases/master-metrics/SKILL.md +72 -0
- package/assets/skills-en/phases/plan-specification/SKILL.md +67 -0
- package/assets/skills-en/phases/security-remediation/SKILL.md +70 -0
- package/assets/skills-en/phases/ship-release/SKILL.md +71 -0
- package/assets/skills-en/reviewers/code-reviewer/SKILL.md +61 -0
- package/assets/skills-en/reviewers/security-auditor/SKILL.md +93 -0
- package/assets/skills-en/reviewers/test-engineer/SKILL.md +66 -0
- package/assets/skills-en/reviewers/web-performance-auditor/SKILL.md +63 -0
- package/dist/adapters/claude-bg.js +193 -15
- package/dist/adapters/codex-controller-worker.js +12 -5
- package/dist/adapters/codex-events.js +16 -4
- package/dist/adapters/codex.js +13 -2
- package/dist/agents-md.js +24 -15
- package/dist/atualizar-branch.js +245 -0
- package/dist/auditoria.js +13 -11
- package/dist/auditrun.js +68 -48
- package/dist/autorizacao-duravel.js +327 -0
- package/dist/board.js +98 -43
- package/dist/branch-de-estado.js +39 -9
- package/dist/busca-semantica.js +7 -5
- package/dist/catalogo.js +35 -1
- package/dist/changelog-reconciliacao.js +290 -0
- package/dist/check-agil.js +500 -0
- package/dist/check-modo.js +41 -0
- package/dist/check-paralelo.js +183 -0
- package/dist/ci.js +255 -46
- package/dist/ciclos.js +8 -6
- package/dist/claim-lint.js +12 -8
- package/dist/claims-conferir.js +350 -0
- package/dist/claims.js +13 -10
- package/dist/company-brain-client.js +45 -3
- package/dist/company-brain-journal.js +2 -1
- package/dist/company-brain-migration.js +47 -6
- package/dist/conducao-batida.js +759 -0
- package/dist/conducao-sentinela.js +405 -0
- package/dist/conducao-texto.js +12 -18
- package/dist/conducao.js +206 -59
- package/dist/conhecimento-consulta.js +270 -0
- package/dist/conhecimento.js +514 -0
- package/dist/conta-esgotada.js +127 -0
- package/dist/dado-privado.js +816 -0
- package/dist/decisao-autonoma.js +5 -4
- package/dist/demo.js +14 -12
- package/dist/dependencias-da-worktree.js +25 -0
- package/dist/divida.js +38 -36
- package/dist/docs.js +103 -82
- package/dist/doctor-forja.js +345 -0
- package/dist/doctor-pulse-cron.js +294 -27
- package/dist/doctor-rede.js +18 -17
- package/dist/doctor.js +278 -116
- package/dist/dsn-da-memoria.js +417 -0
- package/dist/entrega-por-mod.js +503 -0
- package/dist/entrega-pr.js +158 -57
- package/dist/escopo-escrita.js +10 -1
- package/dist/esgotamentos.js +13 -11
- package/dist/estado-thread.js +42 -17
- package/dist/evalrunner.js +35 -32
- package/dist/experiencia-instalacao.js +45 -25
- package/dist/fabrica-batida.js +323 -0
- package/dist/fabrica-estado.js +49 -16
- package/dist/fabrica-publicar.js +3 -1
- package/dist/fila-pesada.js +444 -0
- package/dist/fix.js +24 -21
- package/dist/forja/adaptador.js +6 -0
- package/dist/forja/ambiente-de-ci.js +27 -0
- package/dist/forja/api.js +256 -0
- package/dist/forja/cache.js +89 -0
- package/dist/forja/cli.js +60 -0
- package/dist/forja/contrato.js +9 -0
- package/dist/forja/endereco.js +43 -0
- package/dist/forja/estado.js +22 -0
- package/dist/forja/executor.js +73 -0
- package/dist/forja/github/ci.js +95 -0
- package/dist/forja/github/dono-da-rede.js +18 -0
- package/dist/forja/github/metadados.js +15 -0
- package/dist/forja/github/pedidos.js +200 -0
- package/dist/forja/gitlab/ci.js +141 -0
- package/dist/forja/gitlab/consultas.js +59 -0
- package/dist/forja/gitlab/dono-da-rede.js +43 -0
- package/dist/forja/gitlab/metadados.js +15 -0
- package/dist/forja/gitlab/pedidos-abertos.js +158 -0
- package/dist/forja/gitlab/pedidos.js +141 -0
- package/dist/forja/produtor.js +32 -0
- package/dist/forja/registro.js +31 -0
- package/dist/forja/resolver.js +121 -0
- package/dist/forja/texto.js +13 -0
- package/dist/forja/versao.js +71 -0
- package/dist/forja.js +124 -81
- package/dist/gates.js +3 -28
- package/dist/handoff.js +33 -19
- package/dist/hitl-contract.js +46 -43
- package/dist/hitl-curto.js +37 -25
- package/dist/hitl-gates.js +13 -11
- package/dist/hitl-local-receipt.js +53 -0
- package/dist/hitl-local.js +11 -2
- package/dist/hitl-lock.js +5 -11
- package/dist/hitl-public-receipt.js +11 -0
- package/dist/hitl-resumo.js +72 -3
- package/dist/hitl-sessions.js +62 -9
- package/dist/hitl-tempo-parado.js +4 -4
- package/dist/hitl-texto-livre.js +7 -4
- package/dist/hitl.js +63 -64
- package/dist/horario.js +30 -7
- package/dist/hosts.js +155 -134
- package/dist/impedimento.js +108 -14
- package/dist/index.js +1421 -1019
- package/dist/indice-vetorial.js +14 -12
- package/dist/indice.js +7 -1
- package/dist/init.js +54 -49
- package/dist/instabilidade.js +144 -0
- package/dist/integracoes-locais.js +4 -0
- package/dist/intelligence-graph-cli.js +213 -88
- package/dist/intelligence-graph-contexto.js +56 -17
- package/dist/intelligence-graph-contract.js +2 -1
- package/dist/intelligence-graph-extract-md.js +56 -4
- package/dist/intelligence-graph-extract-ts.js +71 -3
- package/dist/intelligence-graph-query.js +96 -34
- package/dist/leases.js +19 -18
- package/dist/ledger-stats.js +17 -16
- package/dist/ledger.js +46 -2
- package/dist/licoes.js +12 -11
- package/dist/liveness.js +6 -3
- package/dist/locale-accounts.js +220 -0
- package/dist/locale-adapter.js +291 -0
- package/dist/locale-ajuda.js +833 -0
- package/dist/locale-audit.js +199 -0
- package/dist/locale-claims.js +213 -0
- package/dist/locale-conducao.js +138 -0
- package/dist/locale-conhecimento.js +181 -0
- package/dist/locale-decisao.js +205 -0
- package/dist/locale-docs.js +230 -0
- package/dist/locale-estado.js +101 -0
- package/dist/locale-forja.js +290 -0
- package/dist/locale-gerados.js +165 -0
- package/dist/locale-grafo.js +129 -0
- package/dist/locale-master.js +182 -0
- package/dist/locale-network.js +524 -0
- package/dist/locale-onboarding.js +92 -0
- package/dist/locale-operacao-restante.js +931 -0
- package/dist/locale-phase.js +151 -0
- package/dist/locale-projetos.js +100 -0
- package/dist/locale-prompts.js +194 -0
- package/dist/locale-pulse.js +349 -0
- package/dist/locale-reservas.js +87 -0
- package/dist/locale-retry.js +524 -0
- package/dist/locale-roadmap.js +102 -0
- package/dist/locale-security-scan.js +69 -0
- package/dist/locale-setup.js +192 -0
- package/dist/locale-ship.js +597 -0
- package/dist/locale-thread.js +421 -0
- package/dist/locale-verify.js +201 -0
- package/dist/locale-worktree.js +246 -0
- package/dist/locale.js +1258 -0
- package/dist/maestro-runtime.js +22 -3
- package/dist/manifest.js +415 -13
- package/dist/maquina-ociosa.js +211 -0
- package/dist/maquina.js +31 -7
- package/dist/master-digest.js +6 -1
- package/dist/master-nota-local.js +200 -0
- package/dist/master-nota.js +14 -8
- package/dist/master.js +139 -65
- package/dist/mcp-artifacts.js +11 -2
- package/dist/mcp-conhecimento.js +52 -0
- package/dist/mcp-experiencia.js +26 -5
- package/dist/mcp-git.js +23 -6
- package/dist/mcp-grafo.js +7 -4
- package/dist/mcp-install.js +73 -8
- package/dist/mcp-noctum.js +101 -0
- package/dist/mcp-server.js +90 -16
- package/dist/mcp-ship-transporte.js +977 -0
- package/dist/mcp-ship.js +71 -25
- package/dist/mcp-transportes.js +17 -0
- package/dist/mcp-verify.js +6 -1
- package/dist/memoria.js +63 -58
- package/dist/modos-migracao.js +8 -6
- package/dist/modos.js +20 -24
- package/dist/network-roadmap.js +90 -51
- package/dist/noctum-aceite.js +789 -0
- package/dist/noctum-arco.js +1418 -0
- package/dist/noctum-capacidade.js +141 -0
- package/dist/noctum-changelog.js +399 -0
- package/dist/noctum-cli.js +482 -0
- package/dist/noctum-coordenacao.js +1225 -0
- package/dist/noctum-entrega.js +1169 -0
- package/dist/noctum-envelope.js +1046 -0
- package/dist/noctum-execucao.js +376 -0
- package/dist/noctum-fechamento.js +405 -0
- package/dist/noctum-fila.js +314 -0
- package/dist/noctum-guarda.js +256 -0
- package/dist/noctum-integracao.js +507 -0
- package/dist/noctum-maquinas.js +383 -0
- package/dist/noctum-medicao.js +450 -0
- package/dist/noctum-modo.js +362 -0
- package/dist/noctum-pergunta-canal.js +96 -0
- package/dist/noctum-pergunta.js +115 -0
- package/dist/noctum-provas.js +838 -0
- package/dist/noctum-reconciliacao.js +591 -0
- package/dist/noctum-registro.js +215 -0
- package/dist/noctum-relatorio.js +827 -0
- package/dist/noctum-resposta.js +207 -0
- package/dist/noctum-sessoes-em-voo.js +93 -0
- package/dist/noctum-supervisao.js +649 -0
- package/dist/noctum-terminal.js +459 -0
- package/dist/noctum-transporte.js +655 -0
- package/dist/noctum-vigia.js +639 -0
- package/dist/ocupacao.js +32 -3
- package/dist/onboarding.js +56 -26
- package/dist/orkmind.js +48 -25
- package/dist/orquestracao.js +44 -47
- package/dist/parado-no-condutor.js +174 -127
- package/dist/phase.js +144 -63
- package/dist/policies.js +37 -4
- package/dist/portfolio.js +4 -2
- package/dist/postura-local.js +235 -0
- package/dist/preflight.js +11 -10
- package/dist/procedencia.js +220 -0
- package/dist/projeto-alvo.js +186 -24
- package/dist/prompts.js +140 -42
- package/dist/prova-minima.js +5 -6
- package/dist/pulse-cadencia.js +5 -4
- package/dist/pulse-consentimento.js +53 -1
- package/dist/pulse-delivery.js +151 -24
- package/dist/pulse-resposta.js +368 -4
- package/dist/pulse.js +188 -22
- package/dist/ratelimit.js +16 -6
- package/dist/recall.js +14 -13
- package/dist/redacao-saida.js +63 -0
- package/dist/rede-adesao.js +13 -8
- package/dist/rede-forja.js +108 -24
- package/dist/rede-status.js +66 -47
- package/dist/rede.js +86 -46
- package/dist/retomada-checkpoint.js +101 -0
- package/dist/retry.js +451 -212
- package/dist/roadmap-reservas.js +45 -28
- package/dist/roadmap-status.js +129 -33
- package/dist/runtime-ambiente.js +5 -0
- package/dist/runtime-context.js +284 -32
- package/dist/runtime-escada.js +423 -0
- package/dist/runtime-profiles.js +103 -46
- package/dist/runtimes.js +2 -1
- package/dist/saida-segura.js +3 -2
- package/dist/security-scan/agil.js +355 -0
- package/dist/security-scan/cli.js +76 -0
- package/dist/security-scan/compare.js +149 -0
- package/dist/security-scan/config.js +93 -0
- package/dist/security-scan/detect.js +97 -0
- package/dist/security-scan/executor.js +188 -0
- package/dist/security-scan/guard.js +180 -0
- package/dist/security-scan/imagem-de-lint.js +96 -0
- package/dist/security-scan/normalize/actionlint.js +12 -0
- package/dist/security-scan/normalize/eslint.js +12 -0
- package/dist/security-scan/normalize/gitleaks.js +13 -0
- package/dist/security-scan/normalize/golangci-lint.js +13 -0
- package/dist/security-scan/normalize/govulncheck.js +36 -0
- package/dist/security-scan/normalize/index.js +25 -0
- package/dist/security-scan/normalize/npm-audit.js +30 -0
- package/dist/security-scan/normalize/osv-scanner.js +32 -0
- package/dist/security-scan/normalize/oxlint.js +51 -0
- package/dist/security-scan/normalize/pip-audit.js +16 -0
- package/dist/security-scan/normalize/ruff.js +12 -0
- package/dist/security-scan/normalize/semgrep.js +20 -0
- package/dist/security-scan/normalize/trivy.js +33 -0
- package/dist/security-scan/normalize/util.js +204 -0
- package/dist/security-scan/normalize/zizmor.js +19 -0
- package/dist/security-scan/report.js +156 -0
- package/dist/security-scan/run.js +448 -0
- package/dist/security-scan/tools.js +62 -0
- package/dist/security-scan/trabalhador.js +74 -0
- package/dist/security-scan/types.js +15 -0
- package/dist/session-events.js +26 -5
- package/dist/session-watcher-claude.js +81 -20
- package/dist/session-watcher.js +340 -42
- package/dist/sessoes-contas.js +10 -1
- package/dist/sessoes-inventario.js +1 -1
- package/dist/sessoes.js +25 -23
- package/dist/setup.js +59 -58
- package/dist/ship-publicar.js +971 -0
- package/dist/ship.js +243 -97
- package/dist/steal.js +77 -0
- package/dist/superficie.js +14 -11
- package/dist/thread-close.js +12 -11
- package/dist/thread.js +274 -100
- package/dist/tokens.js +15 -11
- package/dist/trabalho-thread.js +297 -0
- package/dist/transporte-do-canal.js +189 -0
- package/dist/types.js +12 -1
- package/dist/util.js +13 -3
- package/dist/varredura-acoes.js +507 -0
- package/dist/verify.js +608 -76
- package/dist/vida-da-thread.js +254 -0
- package/dist/worktree-local.js +167 -0
- package/dist/worktree.js +99 -83
- package/dist/yaml.js +27 -8
- package/eval/casos/check-quality.json +77 -0
- package/eval/casos/security-auditor.json +148 -0
- package/eval/casos/security-remediation.json +161 -0
- package/eval/fixtures/security-scan/README.md +8 -0
- package/eval/fixtures/security-scan/go/.github/workflows/ci.yml +12 -0
- package/eval/fixtures/security-scan/go/Dockerfile +4 -0
- package/eval/fixtures/security-scan/go/README.md +4 -0
- package/eval/fixtures/security-scan/go/config.go +4 -0
- package/eval/fixtures/security-scan/go/go.mod +5 -0
- package/eval/fixtures/security-scan/go/go.sum +2 -0
- package/eval/fixtures/security-scan/go/main.go +31 -0
- package/eval/fixtures/security-scan/js/.github/workflows/ci.yml +12 -0
- package/eval/fixtures/security-scan/js/Dockerfile +3 -0
- package/eval/fixtures/security-scan/js/README.md +4 -0
- package/eval/fixtures/security-scan/js/package-lock.json +844 -0
- package/eval/fixtures/security-scan/js/package.json +10 -0
- package/eval/fixtures/security-scan/js/src/config.js +4 -0
- package/eval/fixtures/security-scan/js/src/server.js +17 -0
- package/eval/fixtures/security-scan/js/src/util.ts +4 -0
- package/eval/fixtures/security-scan/js/tsconfig.json +4 -0
- package/eval/fixtures/security-scan/python/.github/workflows/ci.yml +12 -0
- package/eval/fixtures/security-scan/python/Dockerfile +3 -0
- package/eval/fixtures/security-scan/python/README.md +4 -0
- package/eval/fixtures/security-scan/python/app/__init__.py +0 -0
- package/eval/fixtures/security-scan/python/app/config.py +2 -0
- package/eval/fixtures/security-scan/python/app/main.py +19 -0
- package/eval/fixtures/security-scan/python/requirements.txt +1 -0
- package/package.json +9 -4
- package/references/security-checklist.md +3 -3
- package/schemas/code-artifact-graph.v1.schema.json +2 -1
- package/skills/README.md +1 -0
- package/skills/core/orkastery-bootstrap/SKILL.md +2 -2
- package/skills/phases/check-quality/SKILL.md +16 -0
- package/skills/phases/go-implementation/SKILL.md +6 -0
- package/skills/phases/security-remediation/SKILL.md +70 -0
- package/skills/reviewers/security-auditor/SKILL.md +25 -0
|
@@ -0,0 +1,246 @@
|
|
|
1
|
+
// Os cartoes da Mesa do Dono (RM-059 F5; D4 da thread ork-rm059mesa), em funcoes puras: a fila vira linhas da tela,
|
|
2
|
+
// uma por elemento, e as linhas viram a arvore do painel com os elementos que a superficie resolveu. Cada pergunta
|
|
3
|
+
// traz a recomendada primeiro, com o porque, depois as outras alternativas com a consequencia; a decisao que a
|
|
4
|
+
// fabrica (ou a Mesa) tomou pelo dono traz o que, o porque, como mudar e o custo de reverter. O unico botao com
|
|
5
|
+
// efeito e o Decidir, que so pede ao nucleo o dialogo nativo do pedido. So importa tipos e modulos puros da Mesa.
|
|
6
|
+
import type { MesaDecidindo, MesaDecisao, MesaFila, MesaObvia, MesaOutro, MesaPergunta } from '../types'
|
|
7
|
+
import { duracao, esperamODono, MAXIMO_DE_DECISOES, MAXIMO_DE_OUTROS, TAMANHO_DO_LOTE } from './fila'
|
|
8
|
+
import { FALHA_DA_CONDICAO, testeDaObvia } from './obvia'
|
|
9
|
+
|
|
10
|
+
export type MesaMostrado = { chave: string; thread: string | null; texto: string }
|
|
11
|
+
|
|
12
|
+
export type LinhaDaMesa = {
|
|
13
|
+
tipo: 'titulo' | 'aviso' | 'foco' | 'secao' | 'cabecalho' | 'texto' | 'dim' | 'decidir' | 'nav'
|
|
14
|
+
chave: string
|
|
15
|
+
texto: string
|
|
16
|
+
/** O pedido do botao Decidir. */
|
|
17
|
+
pedidoId?: string
|
|
18
|
+
/** O que vai ao registro do mostrado da ponte quando a linha esta na janela de rolagem do painel a vista. */
|
|
19
|
+
mostrado?: MesaMostrado
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export type EstadoDaMesa = {
|
|
23
|
+
fila: MesaFila
|
|
24
|
+
foco: string | null
|
|
25
|
+
pagina: number
|
|
26
|
+
decidindo: Readonly<Record<string, MesaDecidindo>>
|
|
27
|
+
obvias: Readonly<Record<string, MesaObvia>>
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function plural(n: number, um: string, varios: string): string {
|
|
31
|
+
return `${n} ${n === 1 ? um : varios}`
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/** O prazo como o dono le: o horario local que o nucleo mandou, ou quanto falta. */
|
|
35
|
+
function textoDoPrazo(p: MesaPergunta, agora: number): string | null {
|
|
36
|
+
if (p.prazoLocal) return `prazo ${p.prazoLocal}`
|
|
37
|
+
const t = Date.parse(p.prazo ?? '')
|
|
38
|
+
if (!Number.isFinite(t)) return null
|
|
39
|
+
return t > agora ? `vence em ${duracao(t - agora)}` : `venceu há ${duracao(agora - t)}`
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/** As paginas do lote de perguntas, e a pagina pedida dentro delas. */
|
|
43
|
+
export function paginaDoLote(total: number, pagina: number): { pagina: number; paginas: number } {
|
|
44
|
+
const paginas = Math.max(1, Math.ceil(total / TAMANHO_DO_LOTE))
|
|
45
|
+
return { pagina: Math.min(Math.max(0, Math.floor(pagina) || 0), paginas - 1), paginas }
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/** O cartao de uma pergunta. */
|
|
49
|
+
export function linhasDaPergunta(p: MesaPergunta, e: EstadoDaMesa, agora: number): LinhaDaMesa[] {
|
|
50
|
+
const k = (s: string) => `${p.pedidoId}:${s}`
|
|
51
|
+
const obvia = e.obvias[p.pedidoId]
|
|
52
|
+
const decidida = obvia?.estado === 'registrada'
|
|
53
|
+
const decidindo = e.decidindo[p.pedidoId]
|
|
54
|
+
const cabecalho = [p.thread, p.fase, p.codigo ? `código ${p.codigo}` : null, textoDoPrazo(p, agora)].filter(Boolean).join(' · ')
|
|
55
|
+
const linhas: LinhaDaMesa[] = [{
|
|
56
|
+
tipo: 'cabecalho', chave: k('cabecalho'), texto: `${decidida ? '✓' : '?'} ${cabecalho}`,
|
|
57
|
+
mostrado: { chave: `hitl:${p.pedidoId}`, thread: p.thread, texto: `${p.thread}: espera você: ${p.pergunta}` },
|
|
58
|
+
}]
|
|
59
|
+
linhas.push({ tipo: 'texto', chave: k('pergunta'), texto: ` ${p.pergunta}` })
|
|
60
|
+
if (decidida) {
|
|
61
|
+
linhas.push({ tipo: 'texto', chave: k('obvia'), texto: ` decidido sem te chamar: segue a recomendada no prazo; ${comoMudarDaObvia(p, agora)}` })
|
|
62
|
+
} else if (obvia?.estado === 'incerta') {
|
|
63
|
+
linhas.push({ tipo: 'dim', chave: k('obvia'), texto: ' a Mesa pediu ao núcleo a decisão óbvia e confere na próxima leitura se ela foi gravada' })
|
|
64
|
+
} else if (obvia?.estado === 'recusada') {
|
|
65
|
+
linhas.push({ tipo: 'aviso', chave: k('obvia'), texto: ` a Mesa não decidiu: ${obvia.texto}` })
|
|
66
|
+
} else if (!p.conferida) {
|
|
67
|
+
linhas.push({ tipo: 'dim', chave: k('porque-pergunta'), texto: ' não conferido no núcleo agora: sem Decidir e sem decisão óbvia; use a reserva' })
|
|
68
|
+
} else {
|
|
69
|
+
// Sem criterio escrito e o caso de todo gate que espera o dono: dizer isso em cada cartao so repetiria a regra.
|
|
70
|
+
const teste = testeDaObvia(p, agora)
|
|
71
|
+
const motivo = teste.obvia ? undefined : teste.falhou[0]
|
|
72
|
+
if (motivo && motivo !== 'criterio') linhas.push({ tipo: 'dim', chave: k('porque-pergunta'), texto: ` pergunta: ${FALHA_DA_CONDICAO[motivo]}` })
|
|
73
|
+
}
|
|
74
|
+
const recomendada = p.alternativas.find(a => a.recomendada)
|
|
75
|
+
if (recomendada) {
|
|
76
|
+
linhas.push({ tipo: 'texto', chave: k('recomendada'), texto: ` Recomendada: ${recomendada.chave}) ${recomendada.texto}` })
|
|
77
|
+
if (recomendada.porque) linhas.push({ tipo: 'dim', chave: k('porque'), texto: ` porque: ${recomendada.porque}` })
|
|
78
|
+
if (recomendada.consequencia) linhas.push({ tipo: 'dim', chave: k('consequencia'), texto: ` se escolher: ${recomendada.consequencia}` })
|
|
79
|
+
} else if (p.recomendacao) {
|
|
80
|
+
linhas.push({ tipo: 'texto', chave: k('recomendada'), texto: ` Recomendação: ${p.recomendacao}` })
|
|
81
|
+
}
|
|
82
|
+
for (const a of p.alternativas) {
|
|
83
|
+
if (a.recomendada) continue
|
|
84
|
+
linhas.push({ tipo: 'dim', chave: k(`alt:${a.chave}`), texto: ` ${a.chave}) ${a.texto}${a.consequencia ? `: ${a.consequencia}` : ''}` })
|
|
85
|
+
}
|
|
86
|
+
if (p.irreversivel) linhas.push({ tipo: 'aviso', chave: k('sem-volta'), texto: ` sem volta: ${p.ato ?? 'ato irreversível'}` })
|
|
87
|
+
if (decidindo?.estado === 'abrindo') {
|
|
88
|
+
linhas.push({ tipo: 'texto', chave: k('decidindo'), texto: ' diálogo pedido: responda na janela do Claude Code' })
|
|
89
|
+
} else {
|
|
90
|
+
if (decidindo) linhas.push({ tipo: decidindo.estado === 'erro' ? 'aviso' : 'dim', chave: k('decidindo'), texto: ` ${decidindo.texto}` })
|
|
91
|
+
if (p.canalClaudeCode === 'disponivel') linhas.push({ tipo: 'decidir', chave: `decidir:${p.pedidoId}`, texto: 'Decidir', pedidoId: p.pedidoId })
|
|
92
|
+
}
|
|
93
|
+
if (p.reserva) {
|
|
94
|
+
const telegram = p.contrato === 'ork.hitl/v2' && p.alvo === 'gate' && p.codigo !== null
|
|
95
|
+
linhas.push({ tipo: 'dim', chave: k('reserva'), texto: telegram ? ` reserva: ${p.reserva} pelo Telegram` : ` reserva: ${p.reserva}` })
|
|
96
|
+
}
|
|
97
|
+
return linhas
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/** Como o dono muda a decisao obvia que a Mesa registrou: o Decidir ou a reserva antes do prazo; depois dele, nada. */
|
|
101
|
+
function comoMudarDaObvia(p: MesaPergunta, agora: number): string {
|
|
102
|
+
const prazo = Date.parse(p.prazo ?? '')
|
|
103
|
+
if (Number.isFinite(prazo) && prazo <= agora) return 'o prazo passou e a recomendada seguiu'
|
|
104
|
+
return p.canalClaudeCode === 'disponivel' ? 'para mudar, Decidir antes do prazo' : 'para mudar, use a reserva antes do prazo'
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/** O cartao de uma decisao que a fabrica (ou a Mesa) tomou sem chamar o dono. */
|
|
108
|
+
export function linhasDaDecisao(d: MesaDecisao, agora: number): LinhaDaMesa[] {
|
|
109
|
+
const k = (s: string) => `${d.id}:${s}`
|
|
110
|
+
const em = Date.parse(d.em)
|
|
111
|
+
const quando = Number.isFinite(em) ? ` · há ${duracao(agora - em)}` : ''
|
|
112
|
+
const marca = d.revertidaPor ? ' · revertida' : d.nova ? ' · nova' : ''
|
|
113
|
+
return [
|
|
114
|
+
{ tipo: 'cabecalho', chave: k('cabecalho'), texto: `${d.revertidaPor ? '↺' : '✓'} ${d.thread} · ${d.fase}${quando}${marca}`,
|
|
115
|
+
mostrado: { chave: `decisao:${d.id}`, thread: d.thread || null, texto: `${d.thread}: decidido sem te chamar: ${d.decidido}` } },
|
|
116
|
+
{ tipo: 'texto', chave: k('decidido'), texto: ` ${d.decidido}` },
|
|
117
|
+
...(d.revertidaPor ? [{ tipo: 'aviso' as const, chave: k('revertida'), texto: ` desfeita pela decisão ${d.revertidaPor.slice(0, 8)}` }] : []),
|
|
118
|
+
...(d.reverte ? [{ tipo: 'dim' as const, chave: k('reverte'), texto: ` desfaz a decisão ${d.reverte.slice(0, 8)}` }] : []),
|
|
119
|
+
{ tipo: 'dim', chave: k('porque'), texto: ` porque: ${d.porque}` },
|
|
120
|
+
{ tipo: 'dim', chave: k('como-mudar'), texto: ` como mudar: ${d.comoMudar}` },
|
|
121
|
+
{ tipo: 'dim', chave: k('custo'), texto: ` reverter: agora, ${d.custoAgora}; depois, ${d.custoDepois}` },
|
|
122
|
+
{ tipo: 'dim', chave: k('quem'), texto: ` quem decidiu: ${d.quemDecidiu}` },
|
|
123
|
+
]
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
/** O cartao de uma parada sem pedido ou de um impedimento: so leitura, com a recomendacao e o comando. */
|
|
127
|
+
export function linhasDoOutro(o: MesaOutro, agora: number): LinhaDaMesa[] {
|
|
128
|
+
const k = (s: string) => `${o.chave}:${s}`
|
|
129
|
+
const desde = Date.parse(o.desdeEm ?? '')
|
|
130
|
+
const partes = [o.thread ?? 'sem thread', o.fase, o.motivo, Number.isFinite(desde) ? `há ${duracao(agora - desde)}` : null,
|
|
131
|
+
o.impedimento ? 'só você resolve' : null].filter(Boolean)
|
|
132
|
+
const linhas: LinhaDaMesa[] = [{
|
|
133
|
+
tipo: 'cabecalho', chave: k('cabecalho'), texto: `! ${partes.join(' · ')}`,
|
|
134
|
+
mostrado: { chave: `parada:${o.chave}`, thread: o.thread, texto: `${o.thread ?? 'sem thread'}: ${o.motivo}: ${o.texto}` },
|
|
135
|
+
}]
|
|
136
|
+
if (o.texto) linhas.push({ tipo: 'texto', chave: k('texto'), texto: ` ${o.texto}` })
|
|
137
|
+
if (o.recomendacao) linhas.push({ tipo: 'dim', chave: k('recomendacao'), texto: ` recomendação: ${o.recomendacao}` })
|
|
138
|
+
if (o.comando && o.comando !== o.recomendacao) linhas.push({ tipo: 'dim', chave: k('comando'), texto: ` comando: ${o.comando}` })
|
|
139
|
+
return linhas
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
/** As linhas do painel: o placar, o foco, as perguntas do lote, as decisoes por voce e as paradas. */
|
|
143
|
+
export function linhasDaMesa(e: EstadoDaMesa, opcoes: { agora: number }): LinhaDaMesa[] {
|
|
144
|
+
const { fila, foco } = e
|
|
145
|
+
const agora = opcoes.agora
|
|
146
|
+
const daThread = <T extends { thread: string | null }>(xs: readonly T[]): T[] => (foco ? xs.filter(x => x.thread === foco) : [...xs])
|
|
147
|
+
const perguntas = daThread(fila.perguntas)
|
|
148
|
+
const decisoes = daThread(fila.decisoes)
|
|
149
|
+
const outros = daThread(fila.outros)
|
|
150
|
+
const esperam = esperamODono({ ...fila, perguntas }, e.obvias)
|
|
151
|
+
const desde = esperam.map(p => Date.parse(p.desdeEm ?? '')).filter(Number.isFinite)
|
|
152
|
+
const novas = decisoes.filter(d => d.nova).length
|
|
153
|
+
const placar = [
|
|
154
|
+
`${plural(esperam.length, 'espera', 'esperam')} você${desde.length ? ` (a mais antiga há ${duracao(agora - Math.min(...desde))})` : ''}`,
|
|
155
|
+
`${plural(decisoes.length, 'decidida', 'decididas')} sem te chamar${novas ? ` (${plural(novas, 'nova', 'novas')})` : ''}`,
|
|
156
|
+
plural(outros.length, 'parada', 'paradas'),
|
|
157
|
+
fila.lidoEm === null ? 'ainda não li a fila' : `lido há ${duracao(agora - fila.lidoEm)}`,
|
|
158
|
+
]
|
|
159
|
+
const linhas: LinhaDaMesa[] = [{ tipo: 'titulo', chave: 'titulo', texto: placar.join(' · ') }]
|
|
160
|
+
if (fila.erro) linhas.push({ tipo: 'aviso', chave: 'erro', texto: `não reli a fila: ${fila.erro}` })
|
|
161
|
+
fila.avisos.forEach((a, i) => linhas.push({ tipo: 'aviso', chave: `aviso:${i}`, texto: a }))
|
|
162
|
+
if (foco) linhas.push({ tipo: 'foco', chave: 'foco', texto: `só ${foco}` })
|
|
163
|
+
|
|
164
|
+
// A secao lista todo pedido aberto; o placar conta so os que esperam o dono (a decisao obvia nao espera).
|
|
165
|
+
linhas.push({ tipo: 'secao', chave: 'secao-perguntas', texto: 'Pedidos abertos' })
|
|
166
|
+
if (!perguntas.length) linhas.push({ tipo: 'dim', chave: 'sem-perguntas', texto: ' nenhum pedido aberto agora' })
|
|
167
|
+
const { pagina, paginas } = paginaDoLote(perguntas.length, e.pagina)
|
|
168
|
+
for (const p of perguntas.slice(pagina * TAMANHO_DO_LOTE, (pagina + 1) * TAMANHO_DO_LOTE)) linhas.push(...linhasDaPergunta(p, e, agora))
|
|
169
|
+
if (paginas > 1) linhas.push({ tipo: 'nav', chave: 'nav', texto: `lote ${pagina + 1} de ${paginas}` })
|
|
170
|
+
|
|
171
|
+
linhas.push({ tipo: 'secao', chave: 'secao-decisoes', texto: 'Decididas sem te chamar' })
|
|
172
|
+
if (!decisoes.length) linhas.push({ tipo: 'dim', chave: 'sem-decisoes', texto: ' nenhuma decisão tomada sem te chamar na janela do pulse' })
|
|
173
|
+
for (const d of decisoes.slice(0, MAXIMO_DE_DECISOES)) linhas.push(...linhasDaDecisao(d, agora))
|
|
174
|
+
if (decisoes.length > MAXIMO_DE_DECISOES) {
|
|
175
|
+
linhas.push({ tipo: 'dim', chave: 'mais-decisoes', texto: ` e mais ${decisoes.length - MAXIMO_DE_DECISOES} na janela do pulse (7 dias)` })
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
if (outros.length) {
|
|
179
|
+
linhas.push({ tipo: 'secao', chave: 'secao-outros', texto: 'Paradas e impedimentos' })
|
|
180
|
+
for (const o of outros.slice(0, MAXIMO_DE_OUTROS)) linhas.push(...linhasDoOutro(o, agora))
|
|
181
|
+
if (outros.length > MAXIMO_DE_OUTROS) linhas.push({ tipo: 'dim', chave: 'mais-outros', texto: ` e mais ${outros.length - MAXIMO_DE_OUTROS}` })
|
|
182
|
+
}
|
|
183
|
+
return linhas
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
/**
|
|
187
|
+
* O que o desenho poe na janela de rolagem e vai ao registro do mostrado: so linha com `mostrado` cuja posicao esta
|
|
188
|
+
* entre `offset` e `offset + bodyRows`. Sem janela, nada: o que nao estava na tela nao conta como visto.
|
|
189
|
+
*/
|
|
190
|
+
export function mostradosVisiveis(linhas: readonly LinhaDaMesa[], janela: { offset: number; bodyRows: number } | undefined): MesaMostrado[] {
|
|
191
|
+
if (!janela || !(janela.bodyRows > 0)) return []
|
|
192
|
+
const fim = janela.offset + janela.bodyRows
|
|
193
|
+
return linhas.flatMap((l, i) => (l.mostrado && i >= janela.offset && i < fim ? [l.mostrado] : []))
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
export type AcoesDaMesa = {
|
|
197
|
+
decidir?: (pedidoId: string) => void
|
|
198
|
+
atualizar?: () => void
|
|
199
|
+
verTodas?: () => void
|
|
200
|
+
anteriores?: () => void
|
|
201
|
+
proximas?: () => void
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
/** A arvore do painel com os elementos que a superficie resolveu (`$.ui.resolve(e)` no register.tsx). */
|
|
205
|
+
export function desenharMesa(el: { Box: any; Text: any; Button: any }, linhas: readonly LinhaDaMesa[], acoes: AcoesDaMesa) {
|
|
206
|
+
const { Box, Text, Button } = el
|
|
207
|
+
return (
|
|
208
|
+
<Box flexDirection="column">
|
|
209
|
+
{linhas.map(l => {
|
|
210
|
+
if (l.tipo === 'titulo') {
|
|
211
|
+
return (
|
|
212
|
+
<Box key="titulo" flexDirection="row">
|
|
213
|
+
<Text key="placar" bold wrap="truncate">{l.texto}</Text>
|
|
214
|
+
<Button key="atualizar" label="Atualizar" onPress={() => acoes.atualizar?.()} />
|
|
215
|
+
</Box>
|
|
216
|
+
)
|
|
217
|
+
}
|
|
218
|
+
if (l.tipo === 'foco') {
|
|
219
|
+
return (
|
|
220
|
+
<Box key="foco" flexDirection="row">
|
|
221
|
+
<Text key="foco-texto" dimColor wrap="truncate">{l.texto}</Text>
|
|
222
|
+
<Button key="ver-todas" label="Ver todas" onPress={() => acoes.verTodas?.()} />
|
|
223
|
+
</Box>
|
|
224
|
+
)
|
|
225
|
+
}
|
|
226
|
+
if (l.tipo === 'nav') {
|
|
227
|
+
return (
|
|
228
|
+
<Box key="nav" flexDirection="row">
|
|
229
|
+
<Button key="anteriores" label="Anteriores" onPress={() => acoes.anteriores?.()} />
|
|
230
|
+
<Text key="nav-texto" dimColor wrap="truncate">{l.texto}</Text>
|
|
231
|
+
<Button key="proximas" label="Próximas" onPress={() => acoes.proximas?.()} />
|
|
232
|
+
</Box>
|
|
233
|
+
)
|
|
234
|
+
}
|
|
235
|
+
if (l.tipo === 'decidir') {
|
|
236
|
+
const pedidoId = l.pedidoId ?? ''
|
|
237
|
+
return <Button key={l.chave} label="Decidir" onPress={() => acoes.decidir?.(pedidoId)} />
|
|
238
|
+
}
|
|
239
|
+
if (l.tipo === 'secao' || l.tipo === 'cabecalho') return <Text key={l.chave} bold wrap="truncate">{l.texto}</Text>
|
|
240
|
+
if (l.tipo === 'aviso') return <Text key={l.chave} color="warning" wrap="truncate">{l.texto}</Text>
|
|
241
|
+
if (l.tipo === 'dim') return <Text key={l.chave} dimColor wrap="truncate">{l.texto}</Text>
|
|
242
|
+
return <Text key={l.chave} wrap="truncate">{l.texto}</Text>
|
|
243
|
+
})}
|
|
244
|
+
</Box>
|
|
245
|
+
)
|
|
246
|
+
}
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
// O que a Mesa do Dono manda ao nucleo (RM-059 F5; D6 e D7 da thread ork-rm059mesa), em funcoes puras. Sao so duas
|
|
2
|
+
// ferramentas com efeito e uma de leitura, todas no servidor `orkastery`:
|
|
3
|
+
// - `ork_request_decision` recebe exatamente `threadId` e `pedidoId`: o nucleo abre o dialogo nativo do host e grava
|
|
4
|
+
// a resposta que o dono der, com o recibo do ingresso local. A Mesa nunca manda opcao, letra nem texto.
|
|
5
|
+
// - `ork_decision_record` registra a decisao obvia (teste das quatro condicoes) como decisao autonoma do nucleo,
|
|
6
|
+
// com criterio, evidencia, como mudar e o custo de reverter. Nao responde o pedido.
|
|
7
|
+
// - `ork_hitl_pending` le os pedidos abertos de uma thread.
|
|
8
|
+
// So importa tipos.
|
|
9
|
+
import type { MesaDecidindo, MesaDecisao, MesaPergunta } from '../types'
|
|
10
|
+
|
|
11
|
+
export const SERVIDOR = 'orkastery'
|
|
12
|
+
/** A lista fechada de ferramentas do nucleo que a Mesa chama (D2 e D7). */
|
|
13
|
+
export const FERRAMENTAS_DA_MESA: readonly string[] = ['ork_hitl_pending', 'ork_request_decision', 'ork_decision_record']
|
|
14
|
+
/** Quem decidiu, no rastro do nucleo: a Mesa, nunca o dono (o nucleo recusa rastro que comeca pelo dono). */
|
|
15
|
+
export const QUEM_DECIDIU = 'mod ork-mesa na sessão do dono, pelo teste das quatro condições'
|
|
16
|
+
/** Os tetos do nucleo para a decisao informada (`TETOS_HITL_V2` e o esquema do `ork_decision_record`). */
|
|
17
|
+
export const TETO_DO_CAMPO = 200
|
|
18
|
+
export const TETO_DO_CUSTO = 140
|
|
19
|
+
export const TETO_DA_REFERENCIA = 300
|
|
20
|
+
|
|
21
|
+
/** Uma linha so, cortada no teto, para caber no contrato do nucleo. */
|
|
22
|
+
export function linhaNoTeto(v: string, teto: number): string {
|
|
23
|
+
const t = v.replace(/\s+/g, ' ').trim()
|
|
24
|
+
return t.length <= teto ? t : `${t.slice(0, teto - 1).trimEnd()}…`
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/** Os argumentos do Decidir: so os dois ids, nada que seja resposta. */
|
|
28
|
+
export function argsDoDecidir(p: Pick<MesaPergunta, 'thread' | 'pedidoId'>): { threadId: string; pedidoId: string } {
|
|
29
|
+
return { threadId: p.thread, pedidoId: p.pedidoId }
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/** A marca do pedido no texto da decisao obvia: e por ela que a Mesa sabe que ja decidiu este pedido. */
|
|
33
|
+
export function marcaDoPedido(pedidoId: string): string {
|
|
34
|
+
return `(${pedidoId.slice(0, 8)})`
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/** Ja existe, na janela do pulse, a decisao obvia que a Mesa registrou para este pedido. */
|
|
38
|
+
export function jaDecidida(decisoes: readonly MesaDecisao[], p: Pick<MesaPergunta, 'thread' | 'pedidoId'>): boolean {
|
|
39
|
+
const marca = marcaDoPedido(p.pedidoId)
|
|
40
|
+
return decisoes.some(d => d.thread === p.thread && d.quemDecidiu.startsWith('mod ork-mesa') && d.decidido.includes(marca))
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export type ArgsDaDecisaoObvia = {
|
|
44
|
+
threadId: string
|
|
45
|
+
decidido: string
|
|
46
|
+
porque: string
|
|
47
|
+
comoMudar: string
|
|
48
|
+
custoAgora: string
|
|
49
|
+
custoDepois: string
|
|
50
|
+
criterio: { tipo: 'medicao'; referencia: string }
|
|
51
|
+
quemDecidiu: string
|
|
52
|
+
evidencia: string
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Os argumentos do `ork_decision_record` para uma decisao obvia: o que, o porque, como mudar, o custo de reverter
|
|
57
|
+
* antes e depois do prazo, o criterio (a medicao que mostra o pedido declarando `seguir-recomendada`) e a evidencia
|
|
58
|
+
* (a leitura do `ork_hitl_pending`). Cada campo cabe nos tetos do nucleo, numa linha.
|
|
59
|
+
*/
|
|
60
|
+
export function argsDaDecisaoObvia(p: MesaPergunta, recomendada: { chave: string; texto: string }, referencia: string,
|
|
61
|
+
lidoEmIso: string): ArgsDaDecisaoObvia {
|
|
62
|
+
const pedido = p.codigo ?? p.pedidoId.slice(0, 8)
|
|
63
|
+
const prazo = p.prazoLocal ?? p.prazo ?? 'no prazo do pedido'
|
|
64
|
+
const reserva = p.reserva ? ` ou ${p.reserva} pelo Telegram` : ''
|
|
65
|
+
return {
|
|
66
|
+
threadId: p.thread,
|
|
67
|
+
decidido: linhaNoTeto(`Sem te chamar: o pedido ${pedido} ${marcaDoPedido(p.pedidoId)} da ${p.thread} segue a recomendada ` +
|
|
68
|
+
`${recomendada.chave} (${recomendada.texto}) no prazo ${prazo}`, TETO_DO_CAMPO),
|
|
69
|
+
porque: linhaNoTeto('O pedido declara que segue a recomendada no prazo, é reversível antes da entrega e passou nas quatro ' +
|
|
70
|
+
'condições da decisão óbvia (M4)', TETO_DO_CAMPO),
|
|
71
|
+
comoMudar: linhaNoTeto(`Na Mesa, Decidir no cartão do pedido ${pedido} antes do prazo${reserva}`, TETO_DO_CAMPO),
|
|
72
|
+
custoAgora: linhaNoTeto('Nenhum: o pedido segue aberto até o prazo e o dono escolhe outra alternativa no diálogo', TETO_DO_CUSTO),
|
|
73
|
+
custoDepois: linhaNoTeto('Depois do prazo a recomendada já seguiu; mudar pede nova decisão na thread', TETO_DO_CUSTO),
|
|
74
|
+
criterio: { tipo: 'medicao', referencia: linhaNoTeto(referencia, TETO_DA_REFERENCIA) },
|
|
75
|
+
quemDecidiu: QUEM_DECIDIU,
|
|
76
|
+
evidencia: linhaNoTeto(`ork_hitl_pending ${p.thread} lido em ${lidoEmIso}: pedido ${p.pedidoId}, estado ${p.estado}, ` +
|
|
77
|
+
`acaoPadraoAoExpirar ${p.acaoAoExpirar ?? '-'}, irreversivel ${String(p.irreversivel)}, fase ${p.fase ?? '-'}`, 4096),
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Depois de uma recarga do modulo nenhum Decidir esta em curso: o que ficou `abrindo` no estado vira `sem-resposta`,
|
|
83
|
+
* e o cartao volta a ter o botao.
|
|
84
|
+
*/
|
|
85
|
+
export function soltarAbrindo(decidindo: Readonly<Record<string, MesaDecidindo>>): Record<string, MesaDecidindo> {
|
|
86
|
+
return Object.fromEntries(Object.entries(decidindo).map(([id, v]) => [id, v.estado === 'abrindo'
|
|
87
|
+
? { estado: 'sem-resposta' as const, texto: 'o diálogo ficou sem resposta (a Mesa recarregou); o pedido segue aberto', em: v.em } : v]))
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/** Por que a notificacao do sistema nao saiu, em pt-BR (o motor manda o codigo em ingles). */
|
|
91
|
+
export function motivoDaNotificacao(reason: unknown): string {
|
|
92
|
+
const motivos: Record<string, string> = {
|
|
93
|
+
'no-channel': 'nenhum canal de notificação configurado',
|
|
94
|
+
'no-surface': 'nenhuma superfície para mostrar',
|
|
95
|
+
refused: 'o terminal recusou a notificação',
|
|
96
|
+
disabled: 'as notificações estão desligadas',
|
|
97
|
+
}
|
|
98
|
+
return `a notificação do sistema não saiu (${motivos[String(reason)] ?? String(reason)})`
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/** O que o cartao diz do Decidir, a partir do que o nucleo devolveu (`{ ok, pedidoId, estado, repetida }`). */
|
|
102
|
+
export function resultadoDoDecidir(resposta: unknown, agora: number): MesaDecidindo {
|
|
103
|
+
const r = (resposta && typeof resposta === 'object' ? resposta : {}) as { ok?: unknown; estado?: unknown; repetida?: unknown }
|
|
104
|
+
const estado = typeof r.estado === 'string' ? r.estado : ''
|
|
105
|
+
if (r.ok === true) {
|
|
106
|
+
if (r.repetida === true) return { estado: 'respondido', texto: `o pedido já estava respondido no núcleo: ${estado || 'registrado'}`, em: agora }
|
|
107
|
+
return { estado: 'respondido', texto: `respondido pelo diálogo: ${estado || 'registrado'}`, em: agora }
|
|
108
|
+
}
|
|
109
|
+
return { estado: 'sem-resposta', texto: 'o diálogo fechou sem resposta; o pedido segue aberto', em: agora }
|
|
110
|
+
}
|
|
@@ -0,0 +1,253 @@
|
|
|
1
|
+
// A fila da Mesa do Dono (RM-059 F5), em funcoes puras: o que o pulse, o retrato e o `ork_hitl_pending` do nucleo
|
|
2
|
+
// trazem vira pergunta, parada ou decisao, e a faixa sai daqui. So importa tipos: o register.tsx le o nucleo pela
|
|
3
|
+
// ponte e entrega os objetos a estas funcoes. Nada aqui responde pedido nem escolhe opcao pelo dono.
|
|
4
|
+
import type { MesaAlternativa, MesaCanal, MesaDecisao, MesaFila, MesaObvia, MesaOutro, MesaPergunta } from '../types'
|
|
5
|
+
|
|
6
|
+
/** Perguntas por pagina: o lote de ate cinco da RM-041 e da RM-057. */
|
|
7
|
+
export const TAMANHO_DO_LOTE = 5
|
|
8
|
+
/** Decisoes informadas que o painel mostra por inteiro; o resto vira uma linha com a contagem. */
|
|
9
|
+
export const MAXIMO_DE_DECISOES = 5
|
|
10
|
+
/** Paradas e impedimentos que o painel mostra por inteiro. */
|
|
11
|
+
export const MAXIMO_DE_OUTROS = 5
|
|
12
|
+
/** So a decisao das ultimas 24 h que a Mesa ainda nao mostrou conta como nova. */
|
|
13
|
+
export const JANELA_DE_NOVAS_MS = 24 * 60 * 60 * 1000
|
|
14
|
+
export const TETO_DO_TEXTO = 200
|
|
15
|
+
export const TETO_DA_FAIXA = 200
|
|
16
|
+
const ID = /^[a-zA-Z0-9][a-zA-Z0-9._-]{0,79}$/
|
|
17
|
+
|
|
18
|
+
export const FILA_VAZIA: MesaFila = { perguntas: [], outros: [], decisoes: [], totalDeDecisoes: 0, lidoEm: null, erro: null, avisos: [] }
|
|
19
|
+
|
|
20
|
+
type Bruto = Record<string, unknown>
|
|
21
|
+
const objeto = (v: unknown): v is Bruto => !!v && typeof v === 'object' && !Array.isArray(v)
|
|
22
|
+
const texto = (v: unknown): string => (typeof v === 'string' ? v : '')
|
|
23
|
+
const textoOuNulo = (v: unknown): string | null => (typeof v === 'string' && v.trim() !== '' ? v : null)
|
|
24
|
+
|
|
25
|
+
/** Uma linha so, sem quebra e sem espaco repetido, cortada no teto. */
|
|
26
|
+
export function umaLinha(v: unknown, teto = TETO_DO_TEXTO): string {
|
|
27
|
+
const t = texto(v).replace(/\s+/g, ' ').trim()
|
|
28
|
+
return t.length <= teto ? t : `${t.slice(0, teto - 1).trimEnd()}…`
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/** Quanto tempo, como o dono le: segundos, minutos, horas e dias. */
|
|
32
|
+
export function duracao(ms: number): string {
|
|
33
|
+
const s = Math.max(0, Math.floor(ms / 1000))
|
|
34
|
+
if (s < 60) return `${s} s`
|
|
35
|
+
const min = Math.floor(s / 60)
|
|
36
|
+
if (min < 60) return `${min} min`
|
|
37
|
+
const h = Math.floor(min / 60)
|
|
38
|
+
if (h < 48) return `${h} h`
|
|
39
|
+
return `${Math.floor(h / 24)} dias`
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/** O texto que um JSON traz, ou `undefined` quando nao e JSON. */
|
|
43
|
+
function lerJson(t: string): unknown {
|
|
44
|
+
try {
|
|
45
|
+
return JSON.parse(t)
|
|
46
|
+
} catch {
|
|
47
|
+
return undefined
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/** O JSON que uma ferramenta MCP do nucleo devolve no primeiro bloco de texto; erro vira excecao com o motivo. */
|
|
52
|
+
export function jsonDoMcp(r: unknown): unknown {
|
|
53
|
+
const bruto = (objeto(r) ? r : {}) as { content?: unknown; isError?: unknown }
|
|
54
|
+
const blocos = Array.isArray(bruto.content) ? bruto.content.filter(objeto) : []
|
|
55
|
+
const corpo = blocos.find(c => c.type === 'text' && typeof c.text === 'string')?.text as string | undefined
|
|
56
|
+
if (bruto.isError === true) {
|
|
57
|
+
const j = corpo === undefined ? undefined : lerJson(corpo)
|
|
58
|
+
throw new Error(umaLinha(objeto(j) && typeof j.erro === 'string' ? j.erro : corpo ?? 'o nucleo respondeu com erro'))
|
|
59
|
+
}
|
|
60
|
+
if (corpo === undefined) throw new Error('o nucleo nao devolveu texto')
|
|
61
|
+
const j = lerJson(corpo)
|
|
62
|
+
if (j === undefined) throw new Error('o nucleo devolveu texto que nao e JSON')
|
|
63
|
+
return j
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/** As alternativas de um pedido: letras no `ork.hitl/v2`, numeros no `ork.hitl/v1`, como o nucleo as declara. */
|
|
67
|
+
export function alternativasDoPedido(p: Bruto): MesaAlternativa[] {
|
|
68
|
+
if (p.contrato === 'ork.hitl/v2' && Array.isArray(p.alternativas)) {
|
|
69
|
+
return p.alternativas.filter(objeto).map(a => ({
|
|
70
|
+
chave: texto(a.letra), texto: umaLinha(a.texto), acao: texto(a.acao), consequencia: textoOuNulo(a.consequencia),
|
|
71
|
+
recomendada: a.recomendada === true, porque: textoOuNulo(a.porque),
|
|
72
|
+
}))
|
|
73
|
+
}
|
|
74
|
+
if (Array.isArray(p.opcoes)) {
|
|
75
|
+
return p.opcoes.filter(objeto).map(o => ({
|
|
76
|
+
chave: typeof o.numero === 'number' ? String(o.numero) : texto(o.numero), texto: umaLinha(o.texto), acao: texto(o.acao),
|
|
77
|
+
consequencia: null, recomendada: false, porque: null,
|
|
78
|
+
}))
|
|
79
|
+
}
|
|
80
|
+
return []
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* A pergunta de um pedido aberto (`ork.hitl/v1`, ou `ork.hitl/v2` de classe `pergunta`); `null` para o que nao e
|
|
85
|
+
* pergunta (a decisao informada nao pede nada) ou nao tem identificacao valida.
|
|
86
|
+
*/
|
|
87
|
+
export function perguntaDoPedido(pedido: unknown, extra: { desdeEm?: string | null; reserva?: string | null } = {}): MesaPergunta | null {
|
|
88
|
+
if (!objeto(pedido)) return null
|
|
89
|
+
const v2 = pedido.contrato === 'ork.hitl/v2'
|
|
90
|
+
if (!v2 && pedido.contrato !== 'ork.hitl/v1') return null
|
|
91
|
+
if (v2 && pedido.classe !== 'pergunta') return null
|
|
92
|
+
const alvo = objeto(pedido.alvo) ? pedido.alvo.tipo : undefined
|
|
93
|
+
if (alvo !== 'gate' && alvo !== 'session') return null
|
|
94
|
+
const thread = texto(pedido.thread)
|
|
95
|
+
const id = texto(pedido.id)
|
|
96
|
+
if (!ID.test(thread) || !ID.test(id)) return null
|
|
97
|
+
const alternativas = alternativasDoPedido(pedido)
|
|
98
|
+
const recomendada = alternativas.find(a => a.recomendada)
|
|
99
|
+
const codigo = v2 ? textoOuNulo(pedido.codigo) : null
|
|
100
|
+
return {
|
|
101
|
+
thread, fase: textoOuNulo(pedido.fase), modo: textoOuNulo(pedido.modo), pedidoId: id, codigo,
|
|
102
|
+
contrato: v2 ? 'ork.hitl/v2' : 'ork.hitl/v1', alvo, motivo: texto(pedido.motivo), pergunta: umaLinha(pedido.pergunta),
|
|
103
|
+
recomendacao: v2 ? (recomendada ? `${recomendada.chave}) ${recomendada.texto}` : '') : umaLinha(pedido.recomendacao),
|
|
104
|
+
alternativas, irreversivel: pedido.irreversivel === true, ato: textoOuNulo(pedido.ato),
|
|
105
|
+
tipoDeResposta: v2 ? textoOuNulo(pedido.tipoDeResposta) : null,
|
|
106
|
+
respostaAceita: objeto(pedido.respostaAceita) ? textoOuNulo(pedido.respostaAceita.tipo) : null,
|
|
107
|
+
dependenciaTecnica: pedido.dependenciaTecnica !== undefined, acaoAoExpirar: textoOuNulo(pedido.acaoPadraoAoExpirar),
|
|
108
|
+
prazo: textoOuNulo(pedido.prazo), prazoLocal: null, desdeEm: extra.desdeEm ?? textoOuNulo(pedido.criadoEm),
|
|
109
|
+
estado: 'desconhecido', conferida: false, canalClaudeCode: 'desconhecido',
|
|
110
|
+
reserva: extra.reserva ?? (codigo && recomendada ? `${codigo} ${recomendada.chave}` : null),
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/** As chaves que a Mesa ja registrou na ponte por um canal (`decisao:` no painel, `hitl:` na notificacao), sem o prefixo. */
|
|
115
|
+
function chavesDaMesa(registro: unknown, prefixo: string, canal?: string): Set<string> {
|
|
116
|
+
const itens = objeto(registro) && Array.isArray(registro.itens) ? registro.itens.filter(objeto) : []
|
|
117
|
+
const chaves = new Set<string>()
|
|
118
|
+
for (const i of itens) {
|
|
119
|
+
const chave = texto(i.chave)
|
|
120
|
+
if (i.mod === 'ork-mesa' && chave.startsWith(prefixo) && (canal === undefined || i.canal === canal)) chaves.add(chave.slice(prefixo.length))
|
|
121
|
+
}
|
|
122
|
+
return chaves
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
/** Os ids das decisoes que a Mesa ja mostrou ao dono, pelo registro do mostrado da ponte. */
|
|
126
|
+
export function decisoesVistas(registro: unknown): Set<string> {
|
|
127
|
+
return chavesDaMesa(registro, 'decisao:')
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/** Os pedidos que a Mesa ja levou ao dono por notificacao, pelo registro da ponte: valem depois de uma recarga do modulo. */
|
|
131
|
+
export function pedidosNotificados(registro: unknown): Set<string> {
|
|
132
|
+
return chavesDaMesa(registro, 'hitl:', 'notificacao')
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
/** A ordem das perguntas: o prazo mais perto primeiro, depois a mais antiga; sem prazo vai para o fim. */
|
|
136
|
+
export function ordenarPerguntas(perguntas: readonly MesaPergunta[]): MesaPergunta[] {
|
|
137
|
+
const prazo = (p: MesaPergunta) => {
|
|
138
|
+
const t = Date.parse(p.prazo ?? '')
|
|
139
|
+
return Number.isFinite(t) ? t : Number.POSITIVE_INFINITY
|
|
140
|
+
}
|
|
141
|
+
const desde = (p: MesaPergunta) => {
|
|
142
|
+
const t = Date.parse(p.desdeEm ?? '')
|
|
143
|
+
return Number.isFinite(t) ? t : Number.POSITIVE_INFINITY
|
|
144
|
+
}
|
|
145
|
+
return [...perguntas].sort((a, b) => prazo(a) - prazo(b) || desde(a) - desde(b) || a.pedidoId.localeCompare(b.pedidoId))
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
/**
|
|
149
|
+
* A fila a partir do `ork pulse --json`: as perguntas com pedido, as paradas e os impedimentos de
|
|
150
|
+
* `precisaDeHumanoAgora`, e as decisoes que a fabrica tomou pelo dono na janela do pulse.
|
|
151
|
+
*/
|
|
152
|
+
export function filaDoPulse(pulse: unknown, vistas: ReadonlySet<string>, agora: number): Omit<MesaFila, 'lidoEm' | 'erro' | 'avisos'> {
|
|
153
|
+
const p = objeto(pulse) ? pulse : {}
|
|
154
|
+
const humanos = Array.isArray(p.precisaDeHumanoAgora) ? p.precisaDeHumanoAgora.filter(objeto) : []
|
|
155
|
+
const perguntas: MesaPergunta[] = []
|
|
156
|
+
const outros: MesaOutro[] = []
|
|
157
|
+
for (const item of humanos) {
|
|
158
|
+
const pergunta = item.pedido === undefined ? null
|
|
159
|
+
: perguntaDoPedido(item.pedido, { desdeEm: textoOuNulo(item.desdeEm), reserva: textoOuNulo(item.comandoResposta) })
|
|
160
|
+
if (pergunta) {
|
|
161
|
+
if (!perguntas.some(x => x.pedidoId === pergunta.pedidoId)) perguntas.push(pergunta)
|
|
162
|
+
continue
|
|
163
|
+
}
|
|
164
|
+
if (item.classe === 'score_pendente') continue
|
|
165
|
+
const impedimento = objeto(item.impedimento) ? item.impedimento : null
|
|
166
|
+
outros.push({
|
|
167
|
+
chave: umaLinha(item.id, 160), thread: textoOuNulo(item.thread), fase: textoOuNulo(item.fase), motivo: texto(item.motivo),
|
|
168
|
+
texto: umaLinha(item.pergunta), recomendacao: umaLinha(item.recomendacao),
|
|
169
|
+
comando: umaLinha(impedimento ? impedimento.comando : item.comandoResposta), desdeEm: textoOuNulo(item.desdeEm),
|
|
170
|
+
impedimento: impedimento !== null,
|
|
171
|
+
})
|
|
172
|
+
}
|
|
173
|
+
const brutas = Array.isArray(p.decisoes) ? p.decisoes.filter(objeto) : []
|
|
174
|
+
// A decisao que outra desfez continua na janela, marcada como revertida, e nunca conta como nova.
|
|
175
|
+
const revertidaPor = new Map<string, string>()
|
|
176
|
+
for (const d of brutas) if (typeof d.reverte === 'string' && ID.test(d.reverte)) revertidaPor.set(d.reverte, texto(d.id))
|
|
177
|
+
const decisoes: MesaDecisao[] = brutas.flatMap(d => {
|
|
178
|
+
const id = texto(d.id)
|
|
179
|
+
if (!ID.test(id)) return []
|
|
180
|
+
const custo = objeto(d.custoDeReverter) ? d.custoDeReverter : {}
|
|
181
|
+
const em = texto(d.em)
|
|
182
|
+
const recente = Number.isFinite(Date.parse(em)) && agora - Date.parse(em) <= JANELA_DE_NOVAS_MS
|
|
183
|
+
const desfeita = revertidaPor.get(id) ?? null
|
|
184
|
+
return [{
|
|
185
|
+
id, thread: texto(d.thread), fase: texto(d.fase), em, decidido: umaLinha(d.decidido), porque: umaLinha(d.porque),
|
|
186
|
+
comoMudar: umaLinha(d.comoMudar), custoAgora: umaLinha(custo.agora, 140), custoDepois: umaLinha(custo.depois, 140),
|
|
187
|
+
quemDecidiu: umaLinha(d.quemDecidiu, 120), reverte: typeof d.reverte === 'string' && ID.test(d.reverte) ? d.reverte : null,
|
|
188
|
+
revertidaPor: desfeita, nova: recente && desfeita === null && !vistas.has(id),
|
|
189
|
+
}]
|
|
190
|
+
})
|
|
191
|
+
decisoes.sort((a, b) => Number(b.nova) - Number(a.nova) || b.em.localeCompare(a.em))
|
|
192
|
+
return { perguntas: ordenarPerguntas(perguntas), outros, decisoes, totalDeDecisoes: decisoes.length }
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
/**
|
|
196
|
+
* A assinatura do retrato da secao `hitl`: cada pedido com o estado dele. Muda quando um pedido abre, e respondido,
|
|
197
|
+
* vence ou fica velho, e e o sinal barato para reler o pulse.
|
|
198
|
+
*/
|
|
199
|
+
export function assinaturaDoRetrato(retrato: unknown): string {
|
|
200
|
+
const secoes = objeto(retrato) && objeto(retrato.sections) ? retrato.sections : {}
|
|
201
|
+
const hitl = objeto(secoes.hitl) ? secoes.hitl : {}
|
|
202
|
+
const itens = Array.isArray(hitl.items) ? hitl.items.filter(objeto) : []
|
|
203
|
+
return itens.map(i => `${texto(i.id)}:${texto(i.status)}`).sort().join(',')
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
/**
|
|
207
|
+
* Confere a pergunta no `ork_hitl_pending` da thread: o pedido de agora, o estado, o prazo local e a oferta do canal
|
|
208
|
+
* `claude-code`. Pedido que o nucleo nao lista mais saiu da fila (respondido, substituido ou de outra fase): `null`.
|
|
209
|
+
*/
|
|
210
|
+
export function conferirNoNucleo(pergunta: MesaPergunta, resposta: unknown): MesaPergunta | null {
|
|
211
|
+
const pendencias = objeto(resposta) && Array.isArray(resposta.pendencias) ? resposta.pendencias.filter(objeto) : []
|
|
212
|
+
const achada = pendencias.find(x => objeto(x.pedido) && x.pedido.id === pergunta.pedidoId)
|
|
213
|
+
if (!achada) return null
|
|
214
|
+
const fresca = perguntaDoPedido(achada.pedido, { desdeEm: pergunta.desdeEm, reserva: pergunta.reserva })
|
|
215
|
+
if (!fresca) return null
|
|
216
|
+
const canais = Array.isArray(achada.canais) ? achada.canais.filter(objeto) : []
|
|
217
|
+
const claudeCode = canais.find(c => c.canal === 'claude-code')
|
|
218
|
+
const canal: MesaCanal = claudeCode?.estado === 'disponivel' ? 'disponivel' : 'indisponivel'
|
|
219
|
+
return { ...fresca, estado: textoOuNulo(achada.estado) ?? 'desconhecido', conferida: true, prazoLocal: textoOuNulo(achada.prazoLocal),
|
|
220
|
+
canalClaudeCode: canal }
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
/** As threads que tem pergunta na fila, sem repetir, na ordem da fila. */
|
|
224
|
+
export function threadsComPergunta(perguntas: readonly MesaPergunta[]): string[] {
|
|
225
|
+
return [...new Set(perguntas.map(p => p.thread))]
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
/**
|
|
229
|
+
* O que espera o dono de fato: a pergunta que a Mesa decidiu pelo nucleo nao conta, nem a que ainda confere no nucleo
|
|
230
|
+
* se a decisao chegou (ela volta a esperar o dono se a leitura seguinte nao a mostrar).
|
|
231
|
+
*/
|
|
232
|
+
export function esperamODono(fila: MesaFila, obvias: Readonly<Record<string, MesaObvia>>): MesaPergunta[] {
|
|
233
|
+
return fila.perguntas.filter(p => {
|
|
234
|
+
const estado = obvias[p.pedidoId]?.estado
|
|
235
|
+
return estado !== 'registrada' && estado !== 'incerta'
|
|
236
|
+
})
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
/**
|
|
240
|
+
* A linha da Mesa na faixa: quantos pedidos esperam o dono (com a thread do primeiro), as paradas e as decisoes novas
|
|
241
|
+
* tomadas por ele. Sem idade no texto, para a linha so mudar quando a fila muda. Sem nada, a linha sai.
|
|
242
|
+
*/
|
|
243
|
+
export function textoDaFaixa(fila: MesaFila, obvias: Readonly<Record<string, MesaObvia>>): string | undefined {
|
|
244
|
+
if (fila.lidoEm === null) return undefined
|
|
245
|
+
const partes: string[] = []
|
|
246
|
+
const esperam = esperamODono(fila, obvias)
|
|
247
|
+
if (esperam.length) partes.push(`${esperam.length} ${esperam.length === 1 ? 'espera' : 'esperam'} você (${esperam[0]?.thread})`)
|
|
248
|
+
if (fila.outros.length) partes.push(`${fila.outros.length} ${fila.outros.length === 1 ? 'parada' : 'paradas'}`)
|
|
249
|
+
const novas = fila.decisoes.filter(d => d.nova).length
|
|
250
|
+
if (novas) partes.push(`${novas} ${novas === 1 ? 'decidida' : 'decididas'} sem te chamar`)
|
|
251
|
+
if (!partes.length) return undefined
|
|
252
|
+
return umaLinha(`mesa: ${partes.join(' · ')}`, TETO_DA_FAIXA)
|
|
253
|
+
}
|