@orkastery/cli 0.5.1 → 0.5.6
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/claude-code/README.md +158 -0
- package/adapters/claude-code/agents/ork-master.md +2 -0
- package/adapters/claude-code/commands/master.md +8 -0
- package/adapters/claude-code/commands/ork.md +9 -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-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 +117 -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-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 +18 -3
- package/adapters/hermes/README.md +1 -0
- package/adapters/hermes/bin/ork-network-status.sh +10 -0
- package/adapters/hermes/hermes.plugin.json +1 -0
- package/adapters/hermes/hitl-ingress/__init__.py +2 -2
- package/adapters/hermes/skills/orkastery-devmaster/SKILL.md +7 -6
- package/adapters/openclaw/README.md +26 -5
- package/adapters/openclaw/dist/hitl-ingress.js +2 -2
- package/adapters/openclaw/dist/index.js +46 -11
- package/adapters/openclaw/openclaw.plugin.json +5 -1
- package/adapters/openclaw/package.json +2 -2
- package/adapters/openclaw/src/hitl-ingress.ts +2 -2
- package/adapters/openclaw/src/index.ts +49 -11
- 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/orkmind_bridge.py +117 -9
- package/dist/adapters/claude-bg.js +116 -18
- package/dist/adapters/codex.js +14 -4
- package/dist/agents-md.js +24 -14
- package/dist/auditrun.js +22 -3
- package/dist/board.js +20 -17
- package/dist/branch-de-estado.js +88 -10
- package/dist/busca-semantica.js +52 -12
- package/dist/canarios-rm057.js +149 -0
- package/dist/canarios.js +2 -0
- package/dist/catalogo.js +33 -0
- package/dist/ci.js +21 -8
- package/dist/claims-conferir.js +342 -0
- package/dist/claims.js +41 -1
- package/dist/company-brain-client.js +36 -1
- package/dist/company-brain-context.js +158 -22
- package/dist/company-brain-journal.js +2 -1
- package/dist/company-brain-migration.js +36 -1
- package/dist/conducao-sentinela.js +388 -0
- package/dist/conducao-texto.js +12 -18
- package/dist/conducao.js +122 -30
- package/dist/conhecimento.js +490 -0
- package/dist/docs.js +70 -17
- package/dist/doctor-pulse-cron.js +349 -0
- package/dist/doctor-rede.js +150 -0
- package/dist/doctor.js +195 -111
- package/dist/entrega-pr.js +42 -29
- package/dist/escopo-escrita.js +10 -1
- package/dist/esgotamentos.js +181 -0
- package/dist/estado-thread.js +23 -1
- package/dist/experiencia-instalacao.js +45 -25
- package/dist/experiencia.js +12 -2
- package/dist/fabrica-batida.js +310 -0
- package/dist/fabrica-estado.js +52 -14
- package/dist/fabrica-publicar.js +9 -1
- package/dist/fechamento.js +20 -66
- package/dist/fila-pesada.js +444 -0
- package/dist/fix.js +3 -2
- package/dist/forja.js +31 -7
- package/dist/gates.js +3 -24
- package/dist/handoff.js +10 -6
- package/dist/hitl-contract.js +59 -4
- package/dist/hitl-curto.js +11 -6
- package/dist/hitl-gates.js +5 -0
- package/dist/hitl-local-receipt.js +2 -0
- package/dist/hitl-lote.js +5 -2
- package/dist/hitl-resumo.js +38 -3
- package/dist/hitl-tempo-parado.js +201 -0
- package/dist/hitl-texto-livre.js +5 -5
- package/dist/hitl.js +29 -33
- package/dist/horario.js +41 -7
- package/dist/hosts.js +104 -11
- package/dist/impedimento.js +299 -0
- package/dist/index.js +961 -542
- package/dist/indice-vetorial.js +87 -10
- package/dist/indice.js +9 -1
- package/dist/init.js +56 -43
- package/dist/instabilidade.js +144 -0
- package/dist/integracoes-locais.js +4 -0
- package/dist/intelligence-graph-cli.js +170 -16
- package/dist/intelligence-graph-contexto.js +254 -0
- 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-parsers.js +6 -4
- package/dist/intelligence-graph-query.js +66 -19
- package/dist/leases.js +664 -87
- package/dist/ledger-stats.js +15 -1
- package/dist/ledger.js +18 -1
- package/dist/licoes.js +1 -0
- package/dist/liveness.js +6 -3
- package/dist/locale-ajuda.js +761 -0
- package/dist/locale-gerados.js +160 -0
- package/dist/locale-master.js +99 -0
- package/dist/locale-onboarding.js +92 -0
- package/dist/locale-phase.js +151 -0
- package/dist/locale-pulse.js +211 -0
- package/dist/locale-retry.js +385 -0
- package/dist/locale-roadmap.js +102 -0
- package/dist/locale-ship.js +494 -0
- package/dist/locale-thread.js +277 -0
- package/dist/locale-verify.js +125 -0
- package/dist/locale.js +714 -0
- package/dist/maestro-runtime.js +4 -1
- package/dist/manifest.js +149 -4
- package/dist/maquina.js +23 -4
- package/dist/master-digest.js +6 -1
- package/dist/master-nota.js +18 -11
- package/dist/master.js +145 -31
- package/dist/mcp-artifacts.js +43 -3
- package/dist/mcp-git.js +42 -8
- package/dist/mcp-grafo-worker.js +5 -3
- package/dist/mcp-grafo.js +55 -6
- package/dist/mcp-server.js +22 -3
- package/dist/mcp-ship.js +3 -1
- package/dist/mcp-verify.js +6 -1
- package/dist/memoria.js +48 -6
- package/dist/modos.js +3 -6
- package/dist/network-roadmap.js +196 -18
- package/dist/noctum-arco.js +911 -0
- package/dist/noctum-capacidade.js +141 -0
- package/dist/noctum-envelope.js +810 -0
- package/dist/noctum-execucao.js +304 -0
- package/dist/noctum-fila.js +280 -0
- package/dist/ocupacao.js +40 -5
- package/dist/onboarding.js +40 -21
- package/dist/orkmind.js +99 -7
- package/dist/orquestracao.js +24 -21
- package/dist/parado-no-condutor.js +219 -38
- package/dist/phase.js +160 -52
- package/dist/policies.js +56 -5
- package/dist/portfolio.js +8 -3
- package/dist/postura-local.js +234 -0
- package/dist/preflight.js +11 -10
- package/dist/procedencia.js +225 -0
- package/dist/projeto-alvo.js +132 -5
- package/dist/prompts.js +40 -1
- package/dist/prova-ativacao.js +75 -6
- package/dist/pulse-delivery.js +47 -4
- package/dist/pulse.js +98 -19
- package/dist/ratelimit.js +24 -6
- package/dist/recall.js +3 -1
- package/dist/redacao-saida.js +63 -0
- package/dist/rede-adesao.js +399 -0
- package/dist/rede-forja.js +289 -0
- package/dist/rede-projetos.js +239 -0
- package/dist/rede-status.js +337 -0
- package/dist/rede.js +980 -0
- package/dist/retry.js +291 -140
- package/dist/roadmap-reservas.js +20 -3
- package/dist/roadmap-status.js +150 -34
- package/dist/runtime-context.js +181 -3
- package/dist/runtime-profiles.js +22 -1
- package/dist/runtimes.js +2 -1
- package/dist/saida-segura.js +58 -0
- package/dist/session-events.js +7 -3
- package/dist/session-watcher-claude.js +120 -24
- package/dist/session-watcher.js +185 -33
- package/dist/sessoes-contas.js +187 -0
- package/dist/sessoes-inventario.js +6 -18
- package/dist/sessoes.js +83 -2
- package/dist/ship-publicar.js +800 -0
- package/dist/ship.js +157 -88
- package/dist/steal.js +77 -0
- package/dist/thread-close.js +3 -2
- package/dist/thread.js +299 -49
- package/dist/util.js +8 -1
- package/dist/verify.js +141 -52
- package/dist/vida-da-thread.js +254 -0
- package/dist/worktree-local.js +165 -0
- package/dist/worktree.js +25 -2
- package/dist/yaml.js +27 -8
- package/eval/fixtures/fx-pedido-colado/caso.json +20 -0
- package/package.json +7 -3
- package/references/definition-of-done.md +1 -1
- package/schemas/code-artifact-graph.v1.schema.json +2 -1
- package/skills/README.md +1 -1
- package/skills/core/orkastery-bootstrap/SKILL.md +2 -2
- package/skills/phases/master-metrics/SKILL.md +8 -3
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
// Contrato de tipos do Freio de Lote (RM-059, fatia F2). O Freio nao acrescenta noun ao `$`: este arquivo
|
|
2
|
+
// declara o estado dele (`PluginState['ork-freio']`) e os tipos que os modulos puros e os testes usam.
|
|
3
|
+
// Sem import nem referencia, como o motor exige de um contrato.
|
|
4
|
+
|
|
5
|
+
/** Uma claim ativa da thread, como o Freio a usa: o id, o arquivo e os comandos de verificacao. */
|
|
6
|
+
export type FreioClaim = {
|
|
7
|
+
id: string
|
|
8
|
+
arquivo: string | null
|
|
9
|
+
comandos: string[]
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
/** O que a carga leu do nucleo (T2.1). */
|
|
13
|
+
export type FreioCarga = {
|
|
14
|
+
/** A thread do despacho, do contexto da ponte. */
|
|
15
|
+
thread: string
|
|
16
|
+
/** O status da thread no retrato do nucleo; `fechada` deixa o Freio dormente. */
|
|
17
|
+
statusDaThread: string | null
|
|
18
|
+
/** As claims nao retiradas; `null` enquanto o nucleo nao respondeu. */
|
|
19
|
+
claims: FreioClaim[] | null
|
|
20
|
+
/** Quando as claims foram lidas, em ms desde a epoca. */
|
|
21
|
+
claimsEm: number
|
|
22
|
+
/** O maior numero de claim da thread na leitura, retiradas incluidas (falta na carga gravada antes dele). */
|
|
23
|
+
maiorClaim?: number
|
|
24
|
+
/** A uniao dos `touch_paths` das tarefas do plano; `null` sem plano ou sem nenhum. */
|
|
25
|
+
touchPaths: string[] | null
|
|
26
|
+
/** O plano ja foi lido (com ou sem `touch_paths`). */
|
|
27
|
+
planoLido: boolean
|
|
28
|
+
/** Quando o plano foi lido, em ms desde a epoca. */
|
|
29
|
+
planoEm: number
|
|
30
|
+
/** O que falhou na ultima carga, para a linha de status e o teste. */
|
|
31
|
+
erros: string[]
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/** A foto do `git status` da worktree: o HEAD e a assinatura de cada caminho sujo. */
|
|
35
|
+
export type FreioFoto = {
|
|
36
|
+
head: string | null
|
|
37
|
+
/** Caminho relativo a raiz da sessao para `XY|tamanho|mtimeMs` (ou `XY|ausente`). */
|
|
38
|
+
caminhos: Record<string, string>
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/** Uma claim afetada pelo lote, na fila da conferencia (T2.2). */
|
|
42
|
+
export type FreioPedido = {
|
|
43
|
+
id: string
|
|
44
|
+
/** 3 pelo `arquivo`, 2 pelo caminho citado no comando, 1 pelo nome-base citado (D5). */
|
|
45
|
+
forca: number
|
|
46
|
+
/** Os caminhos tocados que a afetaram. */
|
|
47
|
+
caminhos: string[]
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/** O que o Freio vigia entre os lotes. */
|
|
51
|
+
export type FreioVigia = {
|
|
52
|
+
thread: string
|
|
53
|
+
/** A ultima foto do `git status`; `null` antes da primeira. */
|
|
54
|
+
foto: FreioFoto | null
|
|
55
|
+
/** O numero do lote, que marca quando cada caminho foi tocado. */
|
|
56
|
+
seq: number
|
|
57
|
+
/** O ultimo lote que tocou cada caminho. */
|
|
58
|
+
toques: Record<string, number>
|
|
59
|
+
/** As claims afetadas que esperam conferencia. */
|
|
60
|
+
fila: FreioPedido[]
|
|
61
|
+
/** Os caminhos fora dos `touch_paths` ja avisados nesta sessao. */
|
|
62
|
+
avisadosFora: string[]
|
|
63
|
+
// Os campos da conferencia (T2.3) faltam na vigia gravada antes deles; `comConferencia` os preenche.
|
|
64
|
+
/** A rodada em curso no nucleo, ou `null`. */
|
|
65
|
+
rodada?: FreioRodada | null
|
|
66
|
+
/** O que o lote seguinte devolve ao modelo. */
|
|
67
|
+
entrega?: FreioEntrega
|
|
68
|
+
/** As claims cuja ultima conferencia reprovou. */
|
|
69
|
+
emRisco?: string[]
|
|
70
|
+
/** Ate quando o Freio nao pede conferencia (depois de uma indisponivel), em ms desde a epoca. */
|
|
71
|
+
pausaAte?: number
|
|
72
|
+
/** Por que a ultima conferencia ficou indisponivel, ou `null`. */
|
|
73
|
+
indisponivel?: string | null
|
|
74
|
+
/** O modelo ja foi avisado da conferencia indisponivel nesta sessao. */
|
|
75
|
+
avisouIndisponivel?: boolean
|
|
76
|
+
/** Caminhos fora dos `touch_paths` que o laco principal ainda nao recebeu (lote de subagente nao recebe aviso). */
|
|
77
|
+
foraPendentes?: string[]
|
|
78
|
+
/** As claims cuja falha o modelo ja recebeu: so elas voltam como recuperadas quando passam. */
|
|
79
|
+
avisadas?: string[]
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/** Uma rodada de conferencia: as claims, os caminhos que afetaram cada uma e o lote em que ela comecou (T2.3). */
|
|
83
|
+
export type FreioRodada = {
|
|
84
|
+
ids: string[]
|
|
85
|
+
caminhos: Record<string, string[]>
|
|
86
|
+
/** A forca de cada claim, para ela voltar a fila no mesmo lugar (recusa por claim, conducao alheia). */
|
|
87
|
+
forcas?: Record<string, number>
|
|
88
|
+
/** O numero do lote no inicio; toque mais novo deixa o resultado da claim velho (D6). */
|
|
89
|
+
inicio: number
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/** Uma claim que a conferencia reprovou (`claims.failed`), como volta ao modelo. */
|
|
93
|
+
export type FreioFalha = {
|
|
94
|
+
id: string
|
|
95
|
+
alegacao: string
|
|
96
|
+
arquivo: string | null
|
|
97
|
+
comando: string
|
|
98
|
+
codigo: number | null
|
|
99
|
+
/** O fim da saida, ja redigido pelo nucleo. */
|
|
100
|
+
fim: string
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/** Uma claim cuja conferencia terminou sem veredito (prazo, comando que nao rodou). */
|
|
104
|
+
export type FreioSemVeredito = {
|
|
105
|
+
id: string
|
|
106
|
+
motivo: string
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/** O que o lote seguinte devolve ao modelo. */
|
|
110
|
+
export type FreioEntrega = {
|
|
111
|
+
falhas: FreioFalha[]
|
|
112
|
+
semVeredito: FreioSemVeredito[]
|
|
113
|
+
recuperadas: string[]
|
|
114
|
+
/** O motivo da conferencia indisponivel, so na primeira vez da sessao. */
|
|
115
|
+
indisponivel: string | null
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
declare module 'claude-code' {
|
|
119
|
+
interface PluginState {
|
|
120
|
+
'ork-freio': {
|
|
121
|
+
/** O que a carga leu do nucleo. */
|
|
122
|
+
carga: FreioCarga
|
|
123
|
+
/** O que o Freio vigia entre os lotes. */
|
|
124
|
+
vigia: FreioVigia
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "ork-mesa",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Mesa do Dono do Orkastery no Claude Code: tudo que espera o dono num painel, um cartão por pedido com a recomendação na frente e a decisão pelo diálogo nativo do host. Só decide pelo dono pela decisão autônoma do núcleo, quando a decisão é óbvia, e nunca responde um pedido.",
|
|
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,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
|
+
}
|