@orkastery/cli 0.5.6 → 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 +37 -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/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-pista/hooks/ci.ts +9 -5
- 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/hooks/vigia.ts +1 -1
- 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 +2 -2
- package/adapters/openclaw/README.md +81 -9
- 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/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 +118 -4
- package/dist/adapters/codex-controller-worker.js +12 -5
- package/dist/adapters/codex-events.js +16 -4
- package/dist/adapters/codex.js +3 -1
- package/dist/atualizar-branch.js +245 -0
- package/dist/auditoria.js +13 -11
- package/dist/auditrun.js +51 -46
- package/dist/autorizacao-duravel.js +327 -0
- package/dist/board.js +94 -40
- package/dist/branch-de-estado.js +16 -8
- package/dist/busca-semantica.js +7 -5
- package/dist/catalogo.js +2 -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 +248 -45
- package/dist/ciclos.js +8 -6
- package/dist/claim-lint.js +12 -8
- package/dist/claims-conferir.js +47 -39
- package/dist/claims.js +10 -9
- package/dist/company-brain-client.js +10 -3
- package/dist/company-brain-migration.js +11 -5
- package/dist/conducao-batida.js +759 -0
- package/dist/conducao-sentinela.js +25 -8
- package/dist/conducao.js +84 -29
- package/dist/conhecimento-consulta.js +270 -0
- package/dist/conhecimento.js +111 -87
- 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 +81 -74
- package/dist/doctor-forja.js +345 -0
- package/dist/doctor-pulse-cron.js +113 -1
- package/dist/doctor.js +164 -6
- package/dist/dsn-da-memoria.js +417 -0
- package/dist/entrega-por-mod.js +503 -0
- package/dist/entrega-pr.js +143 -43
- package/dist/esgotamentos.js +13 -11
- package/dist/estado-thread.js +21 -18
- package/dist/evalrunner.js +35 -32
- package/dist/fabrica-batida.js +34 -21
- package/dist/fabrica-estado.js +13 -10
- package/dist/fix.js +21 -19
- 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 +123 -80
- package/dist/handoff.js +23 -13
- 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 +51 -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 +44 -1
- package/dist/hitl-sessions.js +62 -9
- package/dist/hitl-texto-livre.js +7 -4
- package/dist/hitl.js +34 -31
- package/dist/horario.js +16 -0
- package/dist/hosts.js +104 -134
- package/dist/index.js +848 -548
- package/dist/indice-vetorial.js +14 -12
- package/dist/init.js +11 -2
- package/dist/integracoes-locais.js +2 -0
- package/dist/intelligence-graph-cli.js +123 -69
- package/dist/intelligence-graph-query.js +37 -22
- package/dist/leases.js +19 -18
- package/dist/ledger-stats.js +17 -16
- package/dist/ledger.js +31 -1
- package/dist/licoes.js +12 -11
- package/dist/locale-accounts.js +220 -0
- package/dist/locale-adapter.js +291 -0
- package/dist/locale-ajuda.js +76 -4
- 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 +5 -0
- package/dist/locale-grafo.js +129 -0
- package/dist/locale-master.js +83 -0
- package/dist/locale-network.js +524 -0
- package/dist/locale-operacao-restante.js +931 -0
- package/dist/locale-projetos.js +100 -0
- package/dist/locale-prompts.js +194 -0
- package/dist/locale-pulse.js +140 -2
- package/dist/locale-reservas.js +87 -0
- package/dist/locale-retry.js +145 -6
- package/dist/locale-security-scan.js +69 -0
- package/dist/locale-setup.js +192 -0
- package/dist/locale-ship.js +169 -66
- package/dist/locale-thread.js +149 -5
- package/dist/locale-verify.js +80 -4
- package/dist/locale-worktree.js +246 -0
- package/dist/locale.js +553 -9
- package/dist/maestro-runtime.js +22 -3
- package/dist/manifest.js +287 -11
- package/dist/maquina-ociosa.js +211 -0
- package/dist/maquina.js +10 -5
- package/dist/master-nota-local.js +200 -0
- package/dist/master-nota.js +10 -2
- package/dist/master.js +118 -41
- package/dist/mcp-conhecimento.js +52 -0
- package/dist/mcp-experiencia.js +26 -5
- package/dist/mcp-install.js +73 -8
- package/dist/mcp-noctum.js +101 -0
- package/dist/mcp-server.js +82 -14
- package/dist/mcp-ship-transporte.js +977 -0
- package/dist/mcp-ship.js +68 -24
- package/dist/mcp-transportes.js +17 -0
- package/dist/memoria.js +63 -58
- package/dist/modos-migracao.js +8 -6
- package/dist/modos.js +17 -18
- package/dist/network-roadmap.js +85 -49
- package/dist/noctum-aceite.js +789 -0
- package/dist/noctum-arco.js +540 -33
- package/dist/noctum-capacidade.js +3 -3
- 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 +252 -16
- package/dist/noctum-execucao.js +95 -23
- package/dist/noctum-fechamento.js +405 -0
- package/dist/noctum-fila.js +43 -9
- 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 +3 -1
- package/dist/onboarding.js +16 -5
- package/dist/orkmind.js +39 -23
- package/dist/orquestracao.js +31 -35
- package/dist/parado-no-condutor.js +146 -124
- package/dist/phase.js +53 -27
- package/dist/policies.js +33 -0
- package/dist/postura-local.js +4 -3
- package/dist/procedencia.js +9 -14
- package/dist/projeto-alvo.js +59 -20
- package/dist/prompts.js +136 -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 +143 -27
- package/dist/pulse-resposta.js +368 -4
- package/dist/pulse.js +136 -3
- package/dist/ratelimit.js +12 -4
- package/dist/recall.js +14 -13
- package/dist/rede-adesao.js +13 -8
- package/dist/rede-forja.js +108 -24
- package/dist/rede-status.js +63 -45
- package/dist/rede.js +86 -46
- package/dist/retomada-checkpoint.js +101 -0
- package/dist/retry.js +228 -73
- package/dist/roadmap-reservas.js +32 -32
- package/dist/runtime-ambiente.js +5 -0
- package/dist/runtime-context.js +117 -43
- package/dist/runtime-escada.js +423 -0
- package/dist/runtime-profiles.js +103 -46
- 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 +19 -2
- package/dist/session-watcher-claude.js +74 -19
- package/dist/session-watcher.js +156 -10
- package/dist/sessoes-contas.js +9 -0
- package/dist/sessoes-inventario.js +1 -1
- package/dist/sessoes.js +25 -23
- package/dist/setup.js +59 -58
- package/dist/ship-publicar.js +263 -92
- package/dist/ship.js +160 -17
- package/dist/superficie.js +14 -11
- package/dist/thread-close.js +9 -9
- package/dist/thread.js +121 -18
- 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 +5 -2
- package/dist/varredura-acoes.js +507 -0
- package/dist/verify.js +472 -29
- package/dist/worktree-local.js +12 -10
- package/dist/worktree.js +81 -85
- 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/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
package/adapters/README.md
CHANGED
|
@@ -42,7 +42,25 @@ nos perfis `coding` e `messaging` (`toolMetadata`): sem isso, no perfil `coding`
|
|
|
42
42
|
plugin chega ao modelo. `ork_network_status` espera a RM-053 na `main`. `network-roadmap-hosts.test.ts` prova a rota de cada host e o aceite do incidente de 29/09
|
|
43
43
|
pela extensão do OpenClaw com o `ork` real.
|
|
44
44
|
|
|
45
|
+
## Paridade do conhecimento escrito
|
|
46
|
+
|
|
47
|
+
O conhecimento escrito ([RM-026](../docs/roadmap/RM-026-workspace-empresarial.md) K7) chega aos quatro hosts pelas
|
|
48
|
+
mesmas quatro tools `ork_conhecimento_*`: tools do MCP no Claude Code e no Codex, tool argv no OpenClaw (só no
|
|
49
|
+
perfil `coding`) e wrapper no Hermes, cada uma uma chamada de `ork conhecimento <consulta> --json`. A descrição
|
|
50
|
+
das tools do OpenClaw e a skill do Hermes dizem a mesma regra: a lacuna tipada é lacuna, nunca resposta.
|
|
51
|
+
`conhecimento-hosts.test.ts` confere os nomes, o schema e a frase e, com o `ork` real, o mesmo JSON nas três
|
|
52
|
+
superfícies.
|
|
53
|
+
|
|
45
54
|
Usabilidade HITL é prioridade máxima: recomendação, opções claras e canal disponível
|
|
46
55
|
associado ao pedido. MCP local conserva elicitation; Telegram segue disponível
|
|
47
56
|
quando configurado. Ingresso nativo Hermes/Discord e OpenClaw depende de callback
|
|
48
57
|
autenticado e binding privado; terminal Hermes/ACP não está homologado.
|
|
58
|
+
|
|
59
|
+
## Batida do host e canal (RM-063)
|
|
60
|
+
|
|
61
|
+
Os dois hosts de canal ganham a mesma segunda linha da fábrica: `bin/ork-vigia-batida.js` numa
|
|
62
|
+
automação de comando do OpenClaw e `bin/ork-vigia-batida.py` no cron do Hermes leem o
|
|
63
|
+
`ork doctor --json` a cada 30 min e avisam o dono quando a batida passou de 30 min, sem chamada de
|
|
64
|
+
modelo. `#OrkRetomar` e `#OrkFabrica` entram pelo mesmo ingresso assinado do pulse nos dois, e o
|
|
65
|
+
`ork onboarding` grava o `pulse-host.json` do host escolhido. `conducao-canal.test.mjs` e
|
|
66
|
+
`conducao_canal_test.py` provam os dois lados com `ork` e host SIMULADOS.
|
|
@@ -20,6 +20,7 @@
|
|
|
20
20
|
"./skills/phases/goal-definition",
|
|
21
21
|
"./skills/phases/master-metrics",
|
|
22
22
|
"./skills/phases/plan-specification",
|
|
23
|
+
"./skills/phases/security-remediation",
|
|
23
24
|
"./skills/phases/ship-release",
|
|
24
25
|
"./skills/reviewers/code-reviewer",
|
|
25
26
|
"./skills/reviewers/security-auditor",
|
|
@@ -72,11 +72,11 @@ descartável; o recibo traz o sha256 dos arquivos globais antes e depois. Modelo
|
|
|
72
72
|
| `/ork` | Painel: doctor, board, escalonador, modos, fila de score |
|
|
73
73
|
|
|
74
74
|
Mais seis subagentes (`ork-goal`, `ork-plan`, `ork-go`, `ork-check`, `ork-ship`, `ork-master`) para
|
|
75
|
-
isolar o contexto de cada fase, e o
|
|
75
|
+
isolar o contexto de cada fase, o guard `PreToolUse` e o hook `UserPromptSubmit` da resposta curta do dono.
|
|
76
76
|
|
|
77
77
|
## O guard PreToolUse
|
|
78
78
|
|
|
79
|
-
Um hook, em `Bash`, chamando `hooks/ork-guard.js`. Ele bloqueia
|
|
79
|
+
Um hook, em `Bash`, chamando `hooks/ork-guard.js`. Ele bloqueia sete classes de comando:
|
|
80
80
|
|
|
81
81
|
| Bloqueio | Por que |
|
|
82
82
|
|---|---|
|
|
@@ -86,12 +86,44 @@ Um hook, em `Bash`, chamando `hooks/ork-guard.js`. Ele bloqueia seis classes de
|
|
|
86
86
|
| `git push` | Push na mao nao e provado: a entrega passa por `ork ship` |
|
|
87
87
|
| `git reset --hard`, `git clean -fd`, `git checkout -- .` | Descarte em massa sem rastro |
|
|
88
88
|
| `rm -rf` com alvo amplo ou variavel | Apaga o que ninguem pediu |
|
|
89
|
+
| `ork noctum terminal` e o hook da resposta chamados pela ferramenta | A resposta do dono no terminal vem do hook da sessão dele; o agente que chama esse ingresso se passa pelo dono (RM-057) |
|
|
89
90
|
|
|
90
91
|
**O guard nao tem regra de negocio.** Ele nao le ledger, nao sabe quem autorizou o que e nao
|
|
91
92
|
conhece thread nenhuma: ele reconhece a forma do comando e manda o agente pelo caminho que o `ork`
|
|
92
93
|
verifica. Comando que comeca com `ork` passa direto, porque quem prova o push e serializa o merge e
|
|
93
|
-
o nucleo
|
|
94
|
-
|
|
94
|
+
o nucleo; a exceção é o ingresso da resposta do dono (`ork noctum terminal`), barrado antes dessa
|
|
95
|
+
passagem. E se o proprio guard quebrar, ele **libera**: um hook que derruba a sessao por bug proprio
|
|
96
|
+
e pior que nenhum hook. A barreira do ingresso da resposta é lombada, não prova: quem roda como o
|
|
97
|
+
mesmo usuário do sistema a contorna, e o limite está dito abaixo.
|
|
98
|
+
|
|
99
|
+
## A resposta curta do dono no terminal (RM-057)
|
|
100
|
+
|
|
101
|
+
**Desligada.** Desde o GO-FIX 2 da RM-057, o núcleo não grava nada pelo terminal. Numa sessão interativa do
|
|
102
|
+
Claude Code, quando o dono digita a resposta curta a uma pergunta que espera por ele (`sim`, `a` ou
|
|
103
|
+
`<código> a`), o hook `UserPromptSubmit` (`hooks/ork-resposta.js`) leva o evento ao núcleo pelo
|
|
104
|
+
`ork noctum terminal --hook-stdin`, e o núcleo só devolve a recusa `noctum.terminal.desligado`: com alguém
|
|
105
|
+
presente, o dono vê um aviso (`systemMessage`) para responder pelo Telegram, em reply à mensagem da
|
|
106
|
+
pergunta ou com o código e a letra, ou pela janela de confirmação do Claude Code (o diálogo nativo, um
|
|
107
|
+
clique). O prompt segue ao modelo, e nada é gravado nem encaminhado, nem a resposta a um gate. Conversa
|
|
108
|
+
comum nem chama o núcleo, e o prompt que a máquina escreveu (o campo `source` do evento diferente de
|
|
109
|
+
`user`) também não.
|
|
110
|
+
|
|
111
|
+
**Por que desligada:** o Claude Code instalado (2.1.306) ainda não manda o `source` do evento, e sem ele
|
|
112
|
+
nada do dono gravaria. E o agente da própria sessão do dono, pela ferramenta Bash, troca o shell pelo
|
|
113
|
+
núcleo com `exec`, tira a marca `AI_AGENT` e forja o evento com `source` igual a `user`: a cadeia de
|
|
114
|
+
processos e o guard são lombadas, não prova. Ligado, o ingresso não serviria ao dono e serviria a quem
|
|
115
|
+
forja o evento.
|
|
116
|
+
|
|
117
|
+
**O que falta para voltar**, numa fatia própria: o Claude Code mandar a origem humana do evento
|
|
118
|
+
(`source`); uma prova de que o evento veio do hook do harness, e não de um processo do mesmo usuário; e,
|
|
119
|
+
para a palavra solta, a pergunta mostrada exigida como a linha que o núcleo monta para ela, ou só o código
|
|
120
|
+
com a letra. O código do ingresso segue no núcleo atrás da chave `TERMINAL_LIGADO`, desligada; só os
|
|
121
|
+
testes a ligam, e o CLI e o hook nunca.
|
|
122
|
+
|
|
123
|
+
**Limite:** a fronteira é a conta do sistema. No diálogo nativo, qualquer cliente MCP do mesmo usuário que
|
|
124
|
+
declare o formulário passa, sem chave. Para separar o núcleo dos agentes, rode os agentes em outra conta
|
|
125
|
+
ou sandbox. O aviso só aparece depois de reinstalar o plugin (`ork adapter install claude-code`) e do
|
|
126
|
+
`ork` com o `noctum terminal`.
|
|
95
127
|
|
|
96
128
|
## Sensores de sessão
|
|
97
129
|
|
|
@@ -332,7 +364,7 @@ tres e o `--dry-run` mostra o que ele vai fazer sobre cada um.
|
|
|
332
364
|
(`core/`, `phases/`, `reviewers/`, ...) que este catalogo usa. Por isso `plugin.json` declara os
|
|
333
365
|
caminhos um a um. O teste compara esses caminhos com o catálogo fonte e exige
|
|
334
366
|
`onboarding`, acrescentada por I15. Neste catálogo, `claude plugin details orkastery`
|
|
335
|
-
deve reportar
|
|
367
|
+
deve reportar 29 entradas: 21 skills e 8 comandos (a linha `Skills (29)`). O startup registra
|
|
336
368
|
separadamente skills e comandos. As provas anteriores, com 17 e 18 skills, são históricas.
|
|
337
369
|
Confira também os seis agentes e seis eventos de hooks, sem erros de carregamento.
|
|
338
370
|
|
|
@@ -27,6 +27,7 @@ ork phase list <thread> # o que ja aconteceu, com evidencia
|
|
|
27
27
|
|
|
28
28
|
- O modo de conducao afrouxa a PAUSA, nunca a VERIFICACAO.
|
|
29
29
|
- Nada de self-report: toda alegacao vem com o comando que a comprova e a saida real.
|
|
30
|
+
- Esperas: nunca dispare verify, build ou suite com `&` dentro do comando, porque o processo se perde e a sessao espera um aviso que nunca vem; rode o `ork verify` em primeiro plano, com `timeout` (ele espera a vez na fila de recursos pesados), ou use a execucao em segundo plano do host, que avisa quando termina; nunca espere com `pgrep -f` cujo padrao aparece no proprio comando, porque o laco acha a si mesmo e nao termina: espere pelo arquivo de saida com prazo, ou pelo PID guardado.
|
|
30
31
|
- Passo irreversivel (push, merge, delecao) so acontece quando o modo autoriza, e a autorizacao
|
|
31
32
|
fica registrada no ledger.
|
|
32
33
|
- Termine dizendo, em uma linha, em que ponto a thread esta e o que acontece agora.
|
|
@@ -27,6 +27,7 @@ ork phase list <thread> # o que ja aconteceu, com evidencia
|
|
|
27
27
|
|
|
28
28
|
- O modo de conducao afrouxa a PAUSA, nunca a VERIFICACAO.
|
|
29
29
|
- Nada de self-report: toda alegacao vem com o comando que a comprova e a saida real.
|
|
30
|
+
- Esperas: nunca dispare verify, build ou suite com `&` dentro do comando, porque o processo se perde e a sessao espera um aviso que nunca vem; rode o `ork verify` em primeiro plano, com `timeout` (ele espera a vez na fila de recursos pesados), ou use a execucao em segundo plano do host, que avisa quando termina; nunca espere com `pgrep -f` cujo padrao aparece no proprio comando, porque o laco acha a si mesmo e nao termina: espere pelo arquivo de saida com prazo, ou pelo PID guardado.
|
|
30
31
|
- Passo irreversivel (push, merge, delecao) so acontece quando o modo autoriza, e a autorizacao
|
|
31
32
|
fica registrada no ledger.
|
|
32
33
|
- Termine dizendo, em uma linha, em que ponto a thread esta e o que acontece agora.
|
|
@@ -28,6 +28,7 @@ ork phase list <thread> # o que ja aconteceu, com evidencia
|
|
|
28
28
|
|
|
29
29
|
- O modo de conducao afrouxa a PAUSA, nunca a VERIFICACAO.
|
|
30
30
|
- Nada de self-report: toda alegacao vem com o comando que a comprova e a saida real.
|
|
31
|
+
- Esperas: nunca dispare verify, build ou suite com `&` dentro do comando, porque o processo se perde e a sessao espera um aviso que nunca vem; rode o `ork verify` em primeiro plano, com `timeout` (ele espera a vez na fila de recursos pesados), ou use a execucao em segundo plano do host, que avisa quando termina; nunca espere com `pgrep -f` cujo padrao aparece no proprio comando, porque o laco acha a si mesmo e nao termina: espere pelo arquivo de saida com prazo, ou pelo PID guardado.
|
|
31
32
|
- Passo irreversivel (push, merge, delecao) so acontece quando o modo autoriza, e a autorizacao
|
|
32
33
|
fica registrada no ledger.
|
|
33
34
|
- Termine dizendo, em uma linha, em que ponto a thread esta e o que acontece agora.
|
|
@@ -29,6 +29,7 @@ ork phase list <thread> # o que ja aconteceu, com evidencia
|
|
|
29
29
|
|
|
30
30
|
- O modo de conducao afrouxa a PAUSA, nunca a VERIFICACAO.
|
|
31
31
|
- Nada de self-report: toda alegacao vem com o comando que a comprova e a saida real.
|
|
32
|
+
- Esperas: nunca dispare verify, build ou suite com `&` dentro do comando, porque o processo se perde e a sessao espera um aviso que nunca vem; rode o `ork verify` em primeiro plano, com `timeout` (ele espera a vez na fila de recursos pesados), ou use a execucao em segundo plano do host, que avisa quando termina; nunca espere com `pgrep -f` cujo padrao aparece no proprio comando, porque o laco acha a si mesmo e nao termina: espere pelo arquivo de saida com prazo, ou pelo PID guardado.
|
|
32
33
|
- Passo irreversivel (push, merge, delecao) so acontece quando o modo autoriza, e a autorizacao
|
|
33
34
|
fica registrada no ledger.
|
|
34
35
|
- Termine dizendo, em uma linha, em que ponto a thread esta e o que acontece agora.
|
|
@@ -27,6 +27,7 @@ ork phase list <thread> # o que ja aconteceu, com evidencia
|
|
|
27
27
|
|
|
28
28
|
- O modo de conducao afrouxa a PAUSA, nunca a VERIFICACAO.
|
|
29
29
|
- Nada de self-report: toda alegacao vem com o comando que a comprova e a saida real.
|
|
30
|
+
- Esperas: nunca dispare verify, build ou suite com `&` dentro do comando, porque o processo se perde e a sessao espera um aviso que nunca vem; rode o `ork verify` em primeiro plano, com `timeout` (ele espera a vez na fila de recursos pesados), ou use a execucao em segundo plano do host, que avisa quando termina; nunca espere com `pgrep -f` cujo padrao aparece no proprio comando, porque o laco acha a si mesmo e nao termina: espere pelo arquivo de saida com prazo, ou pelo PID guardado.
|
|
30
31
|
- Passo irreversivel (push, merge, delecao) so acontece quando o modo autoriza, e a autorizacao
|
|
31
32
|
fica registrada no ledger.
|
|
32
33
|
- Termine dizendo, em uma linha, em que ponto a thread esta e o que acontece agora.
|
|
@@ -27,6 +27,7 @@ ork phase list <thread> # o que ja aconteceu, com evidencia
|
|
|
27
27
|
|
|
28
28
|
- O modo de conducao afrouxa a PAUSA, nunca a VERIFICACAO.
|
|
29
29
|
- Nada de self-report: toda alegacao vem com o comando que a comprova e a saida real.
|
|
30
|
+
- Esperas: nunca dispare verify, build ou suite com `&` dentro do comando, porque o processo se perde e a sessao espera um aviso que nunca vem; rode o `ork verify` em primeiro plano, com `timeout` (ele espera a vez na fila de recursos pesados), ou use a execucao em segundo plano do host, que avisa quando termina; nunca espere com `pgrep -f` cujo padrao aparece no proprio comando, porque o laco acha a si mesmo e nao termina: espere pelo arquivo de saida com prazo, ou pelo PID guardado.
|
|
30
31
|
- Passo irreversivel (push, merge, delecao) so acontece quando o modo autoriza, e a autorizacao
|
|
31
32
|
fica registrada no ledger.
|
|
32
33
|
- Termine dizendo, em uma linha, em que ponto a thread esta e o que acontece agora.
|
|
@@ -17,6 +17,7 @@
|
|
|
17
17
|
"Notification": [{ "hooks": [{ "type": "command", "command": "node \"${CLAUDE_PLUGIN_ROOT}/hooks/ork-sensor.js\"", "timeout": 5 }] }],
|
|
18
18
|
"Stop": [{ "hooks": [{ "type": "command", "command": "node \"${CLAUDE_PLUGIN_ROOT}/hooks/ork-sensor.js\"", "timeout": 5 }] }],
|
|
19
19
|
"SubagentStop": [{ "hooks": [{ "type": "command", "command": "node \"${CLAUDE_PLUGIN_ROOT}/hooks/ork-sensor.js\"", "timeout": 5 }] }],
|
|
20
|
-
"PostToolUse": [{ "hooks": [{ "type": "command", "command": "node \"${CLAUDE_PLUGIN_ROOT}/hooks/ork-sensor.js\"", "timeout": 5 }] }]
|
|
20
|
+
"PostToolUse": [{ "hooks": [{ "type": "command", "command": "node \"${CLAUDE_PLUGIN_ROOT}/hooks/ork-sensor.js\"", "timeout": 5 }] }],
|
|
21
|
+
"UserPromptSubmit": [{ "hooks": [{ "type": "command", "command": "node \"${CLAUDE_PLUGIN_ROOT}/hooks/ork-resposta.js\"", "timeout": 10 }] }]
|
|
21
22
|
}
|
|
22
23
|
}
|
|
@@ -62,11 +62,25 @@ const BLOQUEIOS = [
|
|
|
62
62
|
},
|
|
63
63
|
];
|
|
64
64
|
|
|
65
|
+
/**
|
|
66
|
+
* RM-057 (resposta curta, D11 da ork-rm057respost): a resposta do dono no terminal vem do hook `UserPromptSubmit` da
|
|
67
|
+
* sessao dele. Um agente que chama esse ingresso pela ferramenta se passa pelo dono, e por isso esta regra roda ANTES da
|
|
68
|
+
* passagem livre do `ork`. E lombada, nao prova: quem roda como o mesmo usuario contorna (o limite dito na RM-058), ate
|
|
69
|
+
* com o subcomando entre aspas. Desde o GO-FIX 2 (D22) o ingresso do terminal esta desligado e nao grava nada.
|
|
70
|
+
*/
|
|
71
|
+
const RESPOSTA_DO_DONO = {
|
|
72
|
+
nome: 'resposta-do-dono-por-ferramenta',
|
|
73
|
+
regex: /\bnoctum\s+terminal\b|ork-resposta\.js/,
|
|
74
|
+
motivo: 'a resposta do dono no terminal vem do hook UserPromptSubmit da sessao dele; o agente que chama esse ingresso se passa pelo dono',
|
|
75
|
+
caminho: 'mostre a pergunta ao dono e espere a resposta dele no Telegram ou pela janela de confirmacao do host; nunca responda por ele',
|
|
76
|
+
};
|
|
77
|
+
|
|
65
78
|
function decidir(entrada) {
|
|
66
79
|
const nome = entrada && entrada.tool_name;
|
|
67
80
|
if (nome !== 'Bash') return null;
|
|
68
81
|
const comando = String((entrada.tool_input && entrada.tool_input.command) || '');
|
|
69
82
|
if (comando.trim() === '') return null;
|
|
83
|
+
if (RESPOSTA_DO_DONO.regex.test(comando)) return RESPOSTA_DO_DONO;
|
|
70
84
|
// Comando do proprio `ork` passa: quem verifica o push provado e o merge serializado e ele.
|
|
71
85
|
if (/^\s*(?:[A-Za-z0-9_]+=\S*\s+)*(?:\S*\/)?ork\s/.test(comando)) return null;
|
|
72
86
|
for (const b of BLOQUEIOS) {
|
|
@@ -127,4 +141,4 @@ process.stdin.on('end', () => {
|
|
|
127
141
|
process.exitCode = codigo;
|
|
128
142
|
});
|
|
129
143
|
|
|
130
|
-
module.exports = { BLOQUEIOS, decidir };
|
|
144
|
+
module.exports = { BLOQUEIOS, RESPOSTA_DO_DONO, decidir };
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
'use strict';
|
|
3
|
+
/**
|
|
4
|
+
* Hook `UserPromptSubmit` do adaptador Claude Code: a resposta curta do dono no terminal (RM-057, ork-rm057respost).
|
|
5
|
+
*
|
|
6
|
+
* GO-FIX 2 (D22): o ingresso do terminal esta DESLIGADO no nucleo. Quando o dono digita a resposta curta a uma pergunta
|
|
7
|
+
* que espera por ele, o nucleo nao grava nada e devolve so a recusa `noctum.terminal.desligado`, que este hook mostra ao
|
|
8
|
+
* dono (`systemMessage`): responda pelo Telegram (em reply a mensagem da pergunta, ou com o codigo) ou pela janela de
|
|
9
|
+
* confirmacao do Claude Code (o dialogo nativo). O transporte abaixo continua o mesmo, para quando uma fatia propria
|
|
10
|
+
* religar o ingresso.
|
|
11
|
+
*
|
|
12
|
+
* O dono aprova onde estiver. Numa sessao interativa, ele responde a pergunta do arco #Noctum com "sim", "a" ou
|
|
13
|
+
* "<codigo> a"; este hook leva o evento ao nucleo (`ork noctum terminal --hook-stdin`) e transporta a decisao dele:
|
|
14
|
+
*
|
|
15
|
+
* - gravada (ou sem efeito, detalhe, ou a pergunta de volta): a frase do nucleo substitui o prompt (`decision: block`),
|
|
16
|
+
* e o "sim" nao chega ao modelo;
|
|
17
|
+
* - resposta a um gate: o contexto vai ao modelo (`additionalContext`), para o condutor abrir o dialogo nativo;
|
|
18
|
+
* - recusa pela origem com alguem presente: um aviso ao dono (`systemMessage`), e o prompt segue ao modelo;
|
|
19
|
+
* - o resto: nada, e o prompt segue como sempre.
|
|
20
|
+
*
|
|
21
|
+
* Ele NAO tem regra de negocio: nao le ledger, nao conhece pergunta e nao prova nada; quem confere a origem da sessao e o
|
|
22
|
+
* nucleo. So texto curto de uma linha chama o nucleo. Erro do hook ou do nucleo nunca bloqueia nem quebra a sessao.
|
|
23
|
+
*
|
|
24
|
+
* GO-FIX 1 (achado 1): o prompt que a maquina injeta na sessao do dono (a tarefa agendada, a volta do /loop, a mensagem
|
|
25
|
+
* de canal ou de outra sessao, o SDK) nunca chega ao nucleo: o evento diz quem o escreveu (`source`), e so `user`, o
|
|
26
|
+
* compositor interativo, passa. Sem o campo (versao do Claude Code que ainda nao o manda), o nucleo recebe o evento,
|
|
27
|
+
* recusa e diz ao dono por que; ele tambem exige `user`, para quem chamar o nucleo sem este hook.
|
|
28
|
+
*/
|
|
29
|
+
const fs = require('node:fs');
|
|
30
|
+
const path = require('node:path');
|
|
31
|
+
const { spawnSync } = require('node:child_process');
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* A forma da resposta curta, larga o bastante para o nucleo decidir e estreita o bastante para conversa comum nem chamar o
|
|
35
|
+
* nucleo: o codigo com a resposta ("NX4K a"), o numero com a letra ("1a") ou uma palavra solta de ate duas palavras e 20
|
|
36
|
+
* caracteres ("sim", "pode seguir"). Tudo numa linha so, ate 64 caracteres.
|
|
37
|
+
*/
|
|
38
|
+
const TETO_DO_PROMPT = 64;
|
|
39
|
+
const CODIGO_E_RESPOSTA = /^[A-HJKMNP-TV-Z](?=[2-9A-HJKMNP-TV-Z]{0,2}[2-9])[2-9A-HJKMNP-TV-Z]{3}\s+\S/i;
|
|
40
|
+
const NUMERO_E_LETRA = /^[0-9]{1,2}\s*[a-e][.!]?$/i;
|
|
41
|
+
const PALAVRA_SOLTA = /^\S+(?:\s\S+)?$/;
|
|
42
|
+
const TETO_DA_ENTRADA = 1024 * 1024;
|
|
43
|
+
|
|
44
|
+
function pareceRespostaCurta(t) {
|
|
45
|
+
if (!t || t.length > TETO_DO_PROMPT || /[\r\n]/.test(t)) return false;
|
|
46
|
+
return CODIGO_E_RESPOSTA.test(t) || NUMERO_E_LETRA.test(t) || (t.length <= 20 && PALAVRA_SOLTA.test(t));
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function lerEntrada() {
|
|
50
|
+
const buffer = Buffer.alloc(TETO_DA_ENTRADA + 1);
|
|
51
|
+
let bytes = 0;
|
|
52
|
+
while (bytes <= TETO_DA_ENTRADA) {
|
|
53
|
+
const n = fs.readSync(0, buffer, bytes, buffer.length - bytes, null);
|
|
54
|
+
if (!n) break;
|
|
55
|
+
bytes += n;
|
|
56
|
+
}
|
|
57
|
+
if (bytes > TETO_DA_ENTRADA) return null;
|
|
58
|
+
return JSON.parse(buffer.subarray(0, bytes).toString('utf8'));
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/** O texto que o nucleo montou, com teto: o hook transporta, nunca reescreve. */
|
|
62
|
+
const texto = (v, teto) => (typeof v === 'string' && v.trim() ? v.slice(0, teto) : '');
|
|
63
|
+
|
|
64
|
+
function executar() {
|
|
65
|
+
const h = lerEntrada();
|
|
66
|
+
if (!h || h.hook_event_name !== 'UserPromptSubmit' || typeof h.prompt !== 'string' ||
|
|
67
|
+
typeof h.cwd !== 'string' || !path.isAbsolute(h.cwd)) return;
|
|
68
|
+
// O prompt que a maquina escreveu nao e do dono: nao chega ao nucleo, e segue ao modelo como sempre.
|
|
69
|
+
if (h.source !== undefined && h.source !== 'user') return;
|
|
70
|
+
if (!pareceRespostaCurta(h.prompt.trim())) return;
|
|
71
|
+
const r = spawnSync(process.env.ORK_SENSOR_CLI || 'ork', ['noctum', 'terminal', '--hook-stdin'], {
|
|
72
|
+
cwd: h.cwd, input: JSON.stringify(h), encoding: 'utf8', timeout: 8000, maxBuffer: 256 * 1024, windowsHide: true,
|
|
73
|
+
});
|
|
74
|
+
if (r.status !== 0 || typeof r.stdout !== 'string' || !r.stdout.trim()) return;
|
|
75
|
+
let d;
|
|
76
|
+
try { d = JSON.parse(r.stdout); } catch { return; }
|
|
77
|
+
if (!d || d.contrato !== 'ork.noctum-terminal/v1') return;
|
|
78
|
+
let saida = null;
|
|
79
|
+
if (['registrada', 'sem-efeito', 'detalhe', 'pergunta'].includes(d.acao) && texto(d.mensagem, 8000)) {
|
|
80
|
+
saida = { decision: 'block', reason: texto(d.mensagem, 8000) };
|
|
81
|
+
} else if (d.acao === 'encaminhar' && texto(d.contexto, 2000)) {
|
|
82
|
+
saida = { hookSpecificOutput: { hookEventName: 'UserPromptSubmit', additionalContext: texto(d.contexto, 2000) } };
|
|
83
|
+
} else if (d.acao === 'recusada' && d.presenca === true && texto(d.mensagem, 2000)) {
|
|
84
|
+
saida = { systemMessage: texto(d.mensagem, 2000) };
|
|
85
|
+
}
|
|
86
|
+
if (saida) process.stdout.write(JSON.stringify(saida) + '\n');
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
try { executar(); }
|
|
90
|
+
catch { process.stderr.write('ork resposta: entrada invalida ou nucleo indisponivel; o prompt segue sem mudanca.\n'); }
|
|
91
|
+
process.exitCode = 0;
|
|
@@ -5,6 +5,7 @@ const fs = require('node:fs');
|
|
|
5
5
|
const path = require('node:path');
|
|
6
6
|
const { spawnSync } = require('node:child_process');
|
|
7
7
|
const { createHash, randomUUID } = require('node:crypto');
|
|
8
|
+
const { accountFailure } = require('./quota-signal');
|
|
8
9
|
|
|
9
10
|
function executar() {
|
|
10
11
|
const limite = 1024 * 1024;
|
|
@@ -25,6 +26,15 @@ function executar() {
|
|
|
25
26
|
if (!tipo) return;
|
|
26
27
|
const payload = { observedAt: new Date().toISOString() };
|
|
27
28
|
if (h.hook_event_name === 'Notification') payload.notificationType = h.notification_type;
|
|
29
|
+
// RM-037 (CHECK 3 da ork-rm037defeit2): o Stop conta o trabalho que ainda vai acordar a sessão. Com despertar agendado
|
|
30
|
+
// (CronCreate, ScheduleWakeup, /loop) ou tarefa MCP, o Claude Code mostra a sessão `idle`; só a entrada do Stop separa
|
|
31
|
+
// a sessão encerrada da que vai voltar a escrever. Runtime sem os campos não manda nada, e o ork não supõe zero.
|
|
32
|
+
if (h.hook_event_name === 'Stop') {
|
|
33
|
+
if (Array.isArray(h.background_tasks)) payload.backgroundTasks = h.background_tasks.length;
|
|
34
|
+
if (Array.isArray(h.session_crons)) payload.sessionCrons = h.session_crons.length;
|
|
35
|
+
}
|
|
36
|
+
const falhaDaConta = accountFailure(h);
|
|
37
|
+
if (falhaDaConta && (tipo === 'notification' || tipo === 'stop')) payload.accountFailure = falhaDaConta;
|
|
28
38
|
// tool_use_id identifica replay da mesma ferramenta. Sem id do protocolo,
|
|
29
39
|
// cada invocação é uma ocorrência; stat do transcript não identifica eventos.
|
|
30
40
|
payload.eventId = typeof h.tool_use_id === 'string'
|
|
@@ -47,11 +57,17 @@ function executar() {
|
|
|
47
57
|
}
|
|
48
58
|
}
|
|
49
59
|
}
|
|
50
|
-
const
|
|
60
|
+
const enviar = (p) => spawnSync(process.env.ORK_SENSOR_CLI || 'ork',
|
|
51
61
|
['sessions', 'event', '--tipo', tipo, '--sessao', h.session_id], {
|
|
52
|
-
cwd: h.cwd, input: JSON.stringify(
|
|
62
|
+
cwd: h.cwd, input: JSON.stringify(p), encoding: 'utf8', timeout: 3000,
|
|
53
63
|
maxBuffer: 32768, windowsHide: true,
|
|
54
64
|
});
|
|
65
|
+
let r = enviar(payload);
|
|
66
|
+
// O `ork` de antes das contagens recusa o campo novo antes de gravar qualquer coisa: o Stop vai de novo sem elas, como ia.
|
|
67
|
+
if (r.status === 1 && /^evento recusado: campo de payload inválido: (?:backgroundTasks|sessionCrons)$/m.test(r.stderr || '')) {
|
|
68
|
+
const { backgroundTasks: _tarefas, sessionCrons: _despertares, ...semContagens } = payload;
|
|
69
|
+
r = enviar(semContagens);
|
|
70
|
+
}
|
|
55
71
|
if (r.status !== 0) {
|
|
56
72
|
// Recusa conhecida não identifica o papel da sessão nem comprova ingestão.
|
|
57
73
|
const semVinculo = r.status === 1 && !r.error && !r.stdout?.trim() &&
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* RM-064 T1: reconhece apenas o fato de a conta ter esgotado. O chamador persiste
|
|
5
|
+
* o código devolvido, nunca os textos que serviram de entrada.
|
|
6
|
+
*/
|
|
7
|
+
function accountFailure(h) {
|
|
8
|
+
const partes = [h?.message, h?.reason, h?.stop_reason, h?.error?.message, h?.error?.code]
|
|
9
|
+
.filter((v) => typeof v === 'string').join('\n');
|
|
10
|
+
return /(?:you(?:'|’)ve hit your (?:weekly|session|individual spend|usage) limit|seu limite de uso foi atingido|usage_limit_(?:exceeded|reached)|insufficient_quota|out of credits|spend (?:cap|limit))/i.test(partes)
|
|
11
|
+
? 'runtime.quota-exhausted' : null;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
module.exports = { accountFailure };
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "ork-mensageiro",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Mensageiro de Sessões do Orkastery no Claude Code: leva a resposta que o dono deu pelo diálogo nativo à sessão de fase claude-bg que parou esperando por ele, como mensagem entre sessões, e na sessão de fase só deixa chegar ao modelo a mensagem que o núcleo confirmou como aquela resposta. Nunca responde nem aprova pelo dono.",
|
|
5
|
+
"author": {
|
|
6
|
+
"name": "Orkastery contributors",
|
|
7
|
+
"url": "https://github.com/orkastery"
|
|
8
|
+
},
|
|
9
|
+
"homepage": "https://orkastery.com/",
|
|
10
|
+
"repository": "https://github.com/orkastery/orkastery",
|
|
11
|
+
"license": "MIT",
|
|
12
|
+
"types": "./types/index.d.ts",
|
|
13
|
+
"dependencies": ["ork-ponte"]
|
|
14
|
+
}
|
|
@@ -0,0 +1,215 @@
|
|
|
1
|
+
// O lado do dono do Mensageiro de Sessoes (RM-059, fatia F9, T9.2), em funcoes puras. A sessao do dono que recebeu a
|
|
2
|
+
// resposta pelo dialogo nativo tem a mensagem na caixa de saida do servidor MCP dela (`ork_session_outbox`), so na
|
|
3
|
+
// memoria; o Mensageiro a leva a sessao de fase por `$.session.send`, sem mexer no texto, e anota o envio no nucleo.
|
|
4
|
+
// O que segue incerto noutra sessao do dono sai do retrato da ponte (secao `hitl`). Nada daqui guarda o texto da
|
|
5
|
+
// resposta no estado (D12 da thread ork-rm059mensag). So importa tipos.
|
|
6
|
+
import type { MensageiroDono, MensageiroEntrega, MensageiroEstadoDaEntrega, MensageiroIncerta } from '../types'
|
|
7
|
+
|
|
8
|
+
export const SERVIDOR = 'orkastery'
|
|
9
|
+
/** A ferramenta da caixa de saida, so no servidor do dono do Claude Code (T9.1). */
|
|
10
|
+
export const FERRAMENTA_DA_CAIXA = 'ork_session_outbox'
|
|
11
|
+
/** O contrato da entrega no nucleo (`core/src/entrega-por-mod.ts`), conferido pelo teste do nucleo. */
|
|
12
|
+
export const CONTRATO_DA_ENTREGA = 'ork.session-delivery/v1'
|
|
13
|
+
/** D10: o envio que nao entrou na fila volta depois do recuo; o que entrou e nao foi reservado, depois da espera. */
|
|
14
|
+
export const RECUO_DO_ENVIO_MS = 30000
|
|
15
|
+
export const ESPERA_DA_RESERVA_MS = 60000
|
|
16
|
+
export const MAXIMO_DE_ENVIOS = 10
|
|
17
|
+
/** D21: a reserva sem recibo por este tempo deixa de ser "confirmando" e pede o dono. */
|
|
18
|
+
export const ESPERA_DO_RECIBO_MS = 5 * 60000
|
|
19
|
+
/** A faixa diz "entregue" por este tempo; o painel guarda o que terminou por este outro. */
|
|
20
|
+
export const FAIXA_DA_ENTREGA_MS = 10 * 60000
|
|
21
|
+
export const GUARDA_DO_TERMINADO_MS = 30 * 60000
|
|
22
|
+
export const MAXIMO_DE_ENTREGAS = 50
|
|
23
|
+
export const DONO_VAZIO: MensageiroDono = { entregas: [], incertas: [], lidoEm: null, erro: null }
|
|
24
|
+
|
|
25
|
+
const UUID = /^[0-9a-f]{8}(?:-[0-9a-f]{4}){3}-[0-9a-f]{12}$/
|
|
26
|
+
const ID_DE_THREAD = /^[a-zA-Z0-9][a-zA-Z0-9._-]{0,79}$/
|
|
27
|
+
const VIVOS: readonly MensageiroEstadoDaEntrega[] = ['na-fila', 'a-caminho', 'reservada', 'nao-chegou']
|
|
28
|
+
|
|
29
|
+
/** Uma entrega da caixa de saida como o nucleo a lista, com o ultimo envio em ms. */
|
|
30
|
+
export type EntradaDaCaixa = {
|
|
31
|
+
thread: string; pedidoId: string; envioId: string; sessionId: string
|
|
32
|
+
/** A mensagem inteira, com o cabecalho do nucleo: vai ao `$.session.send` como esta e nunca ao estado. */
|
|
33
|
+
mensagem: string
|
|
34
|
+
tentativas: number
|
|
35
|
+
ultimoEnvio: { em: number; entregue: boolean; motivo: string | null } | null
|
|
36
|
+
reservada: boolean
|
|
37
|
+
/** Quando a entrega entrou na caixa (ms desde a epoca); conta o sem-recibo quando o envio nao foi anotado. */
|
|
38
|
+
criadaEm: number | null
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
const objeto = (v: unknown): v is Record<string, any> => !!v && typeof v === 'object' && !Array.isArray(v)
|
|
42
|
+
const uuid = (v: unknown): v is string => typeof v === 'string' && UUID.test(v)
|
|
43
|
+
|
|
44
|
+
/** Uma linha so, sem caractere de controle e com teto: o que vem do nucleo ou de um erro nunca quebra a tela. */
|
|
45
|
+
export function umaLinha(v: unknown, teto = 200): string {
|
|
46
|
+
const s = typeof v === 'string' ? v : v instanceof Error ? v.message : String(v)
|
|
47
|
+
return Array.from(s.replace(/\p{Cc}/gu, ' ').replace(/\s+/g, ' ').trim()).slice(0, teto).join('')
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/** O JSON que uma ferramenta do nucleo devolveu; a recusa vira erro com o motivo dela. */
|
|
51
|
+
export function jsonDoMcp(r: unknown, ferramenta: string): unknown {
|
|
52
|
+
const o = objeto(r) ? r : {}
|
|
53
|
+
const conteudo = Array.isArray(o.content) ? o.content : []
|
|
54
|
+
const texto = conteudo.filter(c => objeto(c) && c.type === 'text' && typeof c.text === 'string').map(c => c.text).join('')
|
|
55
|
+
let dados: unknown
|
|
56
|
+
try { dados = JSON.parse(texto) } catch { throw new Error(`${ferramenta} respondeu fora do JSON`) }
|
|
57
|
+
if (o.isError === true) throw new Error(`${ferramenta} recusou: ${umaLinha(objeto(dados) && typeof dados.erro === 'string' ? dados.erro : texto)}`)
|
|
58
|
+
return dados
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
function lerEntrada(e: unknown): EntradaDaCaixa[] {
|
|
62
|
+
if (!objeto(e) || typeof e.thread !== 'string' || !ID_DE_THREAD.test(e.thread) || !uuid(e.pedidoId) || !uuid(e.envioId) ||
|
|
63
|
+
!uuid(e.sessionId) || typeof e.mensagem !== 'string' ||
|
|
64
|
+
!e.mensagem.startsWith(`[${CONTRATO_DA_ENTREGA} pedido=${e.pedidoId} entrega=${e.envioId}] `)) return []
|
|
65
|
+
const u = objeto(e.ultimoEnvio) ? e.ultimoEnvio : null
|
|
66
|
+
const em = u && typeof u.em === 'string' ? Date.parse(u.em) : NaN
|
|
67
|
+
const criadaEm = typeof e.criadaEm === 'string' ? Date.parse(e.criadaEm) : NaN
|
|
68
|
+
return [{ thread: e.thread, pedidoId: e.pedidoId, envioId: e.envioId, sessionId: e.sessionId, mensagem: e.mensagem,
|
|
69
|
+
tentativas: Number.isSafeInteger(e.tentativas) && e.tentativas > 0 ? e.tentativas : 0,
|
|
70
|
+
ultimoEnvio: u && Number.isFinite(em) ? { em, entregue: u.entregue === true, motivo: typeof u.motivo === 'string' ? umaLinha(u.motivo) : null } : null,
|
|
71
|
+
reservada: e.reservada === true, criadaEm: Number.isFinite(criadaEm) ? criadaEm : null }]
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/** A caixa de saida do `listar`; o que nao bate com o contrato fica de fora, e a resposta fora do contrato e erro. */
|
|
75
|
+
export function lerCaixa(dados: unknown): EntradaDaCaixa[] {
|
|
76
|
+
if (!objeto(dados) || dados.contrato !== CONTRATO_DA_ENTREGA || !Array.isArray(dados.entregas)) {
|
|
77
|
+
throw new Error('a caixa de saída respondeu fora do contrato ork.session-delivery/v1')
|
|
78
|
+
}
|
|
79
|
+
return dados.entregas.flatMap(lerEntrada)
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/** A entrega que o `enviada` devolveu, com a reserva que ela tinha na lista (o nucleo so a le no `listar`). */
|
|
83
|
+
export function entradaAnotada(dados: unknown, reservada: boolean): EntradaDaCaixa | null {
|
|
84
|
+
const lida = objeto(dados) && dados.contrato === CONTRATO_DA_ENTREGA ? lerEntrada({ ...(objeto(dados.entrega) ? dados.entrega : {}), reservada }) : []
|
|
85
|
+
return lida[0] ?? null
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/** D10: envia o que nunca foi, o que nao entrou na fila depois do recuo e o que entrou e ninguem reservou depois da espera. */
|
|
89
|
+
export function deveEnviar(e: EntradaDaCaixa, agora: number): boolean {
|
|
90
|
+
if (e.reservada || e.tentativas >= MAXIMO_DE_ENVIOS) return false
|
|
91
|
+
if (!e.ultimoEnvio) return true
|
|
92
|
+
return agora - e.ultimoEnvio.em >= (e.ultimoEnvio.entregue ? ESPERA_DA_RESERVA_MS : RECUO_DO_ENVIO_MS)
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/** O argumento do `$.session.send`: a sessao do pedido e a mensagem do nucleo, sem mexer em nada. */
|
|
96
|
+
export function argsDoEnvio(e: EntradaDaCaixa): { to: { sessionId: string }; text: string } {
|
|
97
|
+
return { to: { sessionId: e.sessionId }, text: e.mensagem }
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/** O resultado do `$.session.send` como o nucleo o anota. */
|
|
101
|
+
export function resultadoDoEnvio(r: unknown): { entregue: boolean; motivo: string | null } {
|
|
102
|
+
if (objeto(r) && r.isDelivered === true) return { entregue: true, motivo: null }
|
|
103
|
+
return { entregue: false, motivo: umaLinha(objeto(r) && typeof r.reason === 'string' && r.reason ? r.reason : 'a sessão não recebeu a mensagem') }
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
export function argsDaAnotacao(e: EntradaDaCaixa, r: { entregue: boolean; motivo: string | null }): Record<string, unknown> {
|
|
107
|
+
return { operacao: 'enviada', threadId: e.thread, envioId: e.envioId, entregue: r.entregue, ...(r.motivo ? { motivo: r.motivo } : {}) }
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/** A secao `hitl` do retrato, quando ela veio lida (disponivel ou vazia); `null` quando nao da para concluir nada. */
|
|
111
|
+
function secaoHitl(retrato: unknown): Record<string, any>[] | null {
|
|
112
|
+
const secoes = objeto(retrato) && objeto(retrato.sections) ? retrato.sections : {}
|
|
113
|
+
const hitl = objeto(secoes.hitl) ? secoes.hitl : null
|
|
114
|
+
if (!hitl || (hitl.state !== 'available' && hitl.state !== 'empty')) return null
|
|
115
|
+
return Array.isArray(hitl.items) ? hitl.items.filter(objeto) : []
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
const threadDoItem = (i: Record<string, any>): string | null => {
|
|
119
|
+
const ref = Array.isArray(i.refs) ? i.refs.find((r: unknown) => objeto(r) && r.kind === 'thread' && typeof r.id === 'string') : null
|
|
120
|
+
return ref && ID_DE_THREAD.test(ref.id) ? ref.id : null
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/** Os pedidos abertos que o retrato mostra (respondido sai da secao); `null` quando a secao nao veio lida. */
|
|
124
|
+
export function pedidosNoRetrato(retrato: unknown): Set<string> | null {
|
|
125
|
+
const itens = secaoHitl(retrato)
|
|
126
|
+
return itens === null ? null : new Set(itens.flatMap(i => (typeof i.id === 'string' ? [i.id] : [])))
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
/** Os pedidos de sessao com a entrega incerta no retrato (`delivery.uncertain`): a entrega segue em alguma sessao do dono. */
|
|
130
|
+
export function incertasDoRetrato(retrato: unknown): MensageiroIncerta[] {
|
|
131
|
+
return (secaoHitl(retrato) ?? []).flatMap(i => {
|
|
132
|
+
const thread = threadDoItem(i)
|
|
133
|
+
return i.status === 'delivery.uncertain' && objeto(i.facts) && i.facts.target === 'session' && uuid(i.id) && thread
|
|
134
|
+
? [{ thread, pedidoId: i.id }] : []
|
|
135
|
+
})
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
/** O estado de uma entrega que esta na caixa, agora. */
|
|
139
|
+
export function estadoDaEntrada(e: EntradaDaCaixa, agora: number): MensageiroEstadoDaEntrega {
|
|
140
|
+
if (e.reservada) {
|
|
141
|
+
const desde = e.ultimoEnvio?.em ?? e.criadaEm
|
|
142
|
+
return typeof desde === 'number' && agora - desde >= ESPERA_DO_RECIBO_MS ? 'sem-recibo' : 'reservada'
|
|
143
|
+
}
|
|
144
|
+
if (!e.ultimoEnvio) return 'na-fila'
|
|
145
|
+
if (e.tentativas >= MAXIMO_DE_ENVIOS) return 'desistiu'
|
|
146
|
+
return e.ultimoEnvio.entregue ? 'a-caminho' : 'nao-chegou'
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
function motivoDaEntrada(e: EntradaDaCaixa, estado: MensageiroEstadoDaEntrega): string | null {
|
|
150
|
+
if (estado === 'sem-recibo') return 'a sessão de fase reservou a entrega e não gravou o recibo em 5 min'
|
|
151
|
+
if (estado === 'desistiu') {
|
|
152
|
+
return e.ultimoEnvio?.entregue
|
|
153
|
+
? `a sessão de fase não confirmou a entrega depois de ${e.tentativas} envios`
|
|
154
|
+
: e.ultimoEnvio?.motivo ?? `a mensagem não entrou na fila da sessão em ${e.tentativas} envios`
|
|
155
|
+
}
|
|
156
|
+
return estado === 'nao-chegou' ? e.ultimoEnvio?.motivo ?? null : null
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
/**
|
|
160
|
+
* As entregas desta sessao depois de uma leitura da caixa. A que saiu da caixa terminou: entregue, quando o retrato
|
|
161
|
+
* nao mostra mais o pedido (respondido); "saiu da caixa", quando o pedido segue aberto. Sem o retrato, nada se conclui.
|
|
162
|
+
*/
|
|
163
|
+
export function atualizarEntregas(anteriores: readonly MensageiroEntrega[], caixa: readonly EntradaDaCaixa[],
|
|
164
|
+
noRetrato: Set<string> | null, agora: number): MensageiroEntrega[] {
|
|
165
|
+
const restantes = new Map(anteriores.map(e => [e.envioId, e]))
|
|
166
|
+
const saida: MensageiroEntrega[] = []
|
|
167
|
+
for (const c of caixa) {
|
|
168
|
+
const estado = estadoDaEntrada(c, agora)
|
|
169
|
+
const antes = restantes.get(c.envioId)
|
|
170
|
+
restantes.delete(c.envioId)
|
|
171
|
+
saida.push({ thread: c.thread, pedidoId: c.pedidoId, envioId: c.envioId, sessionId: c.sessionId, estado, tentativas: c.tentativas,
|
|
172
|
+
motivo: motivoDaEntrada(c, estado), em: antes && antes.estado === estado ? antes.em : agora })
|
|
173
|
+
}
|
|
174
|
+
for (const e of restantes.values()) {
|
|
175
|
+
if (!VIVOS.includes(e.estado) && e.estado !== 'desistiu' && e.estado !== 'sem-recibo') {
|
|
176
|
+
if (agora - e.em <= GUARDA_DO_TERMINADO_MS) saida.push(e)
|
|
177
|
+
continue
|
|
178
|
+
}
|
|
179
|
+
if (noRetrato === null) { saida.push(e); continue }
|
|
180
|
+
const aberto = noRetrato.has(e.pedidoId)
|
|
181
|
+
saida.push({ ...e, estado: aberto ? 'saiu-da-caixa' : 'entregue', em: agora,
|
|
182
|
+
motivo: aberto ? 'a caixa de saída soltou a entrega sem recibo; responda pelo claude attach' : null })
|
|
183
|
+
}
|
|
184
|
+
return saida.slice(-MAXIMO_DE_ENTREGAS)
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
/** A thread do ledger que mudou, pelos caminhos que a ponte vigia (`.orkastery/threads/<thread>/ledger.jsonl`). */
|
|
188
|
+
export function threadDoLedger(caminho: unknown): string | null {
|
|
189
|
+
if (typeof caminho !== 'string') return null
|
|
190
|
+
const m = /\.orkastery\/threads\/([^/]+)\/ledger\.jsonl$/.exec(caminho)
|
|
191
|
+
return m && ID_DE_THREAD.test(m[1]) ? m[1] : null
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
function daThread(entregas: readonly MensageiroEntrega[], um: string, varios: (n: number) => string): string {
|
|
195
|
+
return entregas.length === 1 ? `${um} ${entregas[0].thread}` : varios(entregas.length)
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
/**
|
|
199
|
+
* A linha da faixa (D7 do plano dos mods): o que nao chegou e precisa do dono vem primeiro; depois o que esta a caminho;
|
|
200
|
+
* depois o que acabou de ser entregue. Sem nada disso, a linha sai da faixa.
|
|
201
|
+
*/
|
|
202
|
+
export function textoDaFaixa(dono: MensageiroDono, agora: number): string | undefined {
|
|
203
|
+
const falhas = dono.entregas.filter(e => e.estado === 'desistiu' || e.estado === 'saiu-da-caixa')
|
|
204
|
+
if (falhas.length) return falhas.length === 1
|
|
205
|
+
? `a resposta para a ${falhas[0].thread} não chegou; responda pelo claude attach` : `${falhas.length} respostas não chegaram; abra /mensageiro`
|
|
206
|
+
// Sem recibo, a resposta pode ter entrado na conversa: o dono confere antes de responder de novo.
|
|
207
|
+
const semRecibo = dono.entregas.filter(e => e.estado === 'sem-recibo')
|
|
208
|
+
if (semRecibo.length) return semRecibo.length === 1
|
|
209
|
+
? `a resposta para a ${semRecibo[0].thread} ficou sem recibo; confira pelo claude attach` : `${semRecibo.length} respostas sem recibo; abra /mensageiro`
|
|
210
|
+
const vivas = dono.entregas.filter(e => VIVOS.includes(e.estado))
|
|
211
|
+
if (vivas.length) return daThread(vivas, 'resposta a caminho da', n => `${n} respostas a caminho das sessões de fase`)
|
|
212
|
+
const entregues = dono.entregas.filter(e => e.estado === 'entregue' && agora - e.em <= FAIXA_DA_ENTREGA_MS)
|
|
213
|
+
if (entregues.length) return daThread(entregues, 'resposta entregue à', n => `${n} respostas entregues às sessões de fase`)
|
|
214
|
+
return undefined
|
|
215
|
+
}
|