@orkastery/cli 0.6.0 → 0.6.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (118) hide show
  1. package/adapters/claude-code/agents/ork-check.md +1 -1
  2. package/adapters/claude-code/agents/ork-go.md +1 -1
  3. package/adapters/claude-code/agents/ork-goal.md +1 -1
  4. package/adapters/claude-code/agents/ork-master.md +1 -1
  5. package/adapters/claude-code/agents/ork-plan.md +1 -1
  6. package/adapters/claude-code/agents/ork-ship.md +1 -1
  7. package/adapters/claude-code/commands/ork.md +1 -1
  8. package/adapters/claude-code/mods/ork-torre/hooks/frases.ts +2 -0
  9. package/adapters/codex/skills/ork/SKILL.md +1 -1
  10. package/adapters/openclaw/openclaw.plugin.json +1 -1
  11. package/adapters/openclaw/package.json +1 -1
  12. package/assets/adapters-en/claude-code/agents/ork-check.md +1 -1
  13. package/assets/adapters-en/claude-code/agents/ork-go.md +1 -1
  14. package/assets/adapters-en/claude-code/agents/ork-goal.md +1 -1
  15. package/assets/adapters-en/claude-code/agents/ork-master.md +1 -1
  16. package/assets/adapters-en/claude-code/agents/ork-plan.md +1 -1
  17. package/assets/adapters-en/claude-code/agents/ork-ship.md +1 -1
  18. package/assets/adapters-en/claude-code/commands/ork.md +1 -1
  19. package/assets/adapters-en/codex/skills/ork/SKILL.md +1 -1
  20. package/assets/orkmind_fixture.py +4 -1
  21. package/assets/skills-en/core/orkastery-bootstrap/SKILL.md +1 -1
  22. package/assets/skills-en/phases/check-quality/SKILL.md +1 -1
  23. package/assets/skills-en/phases/go-implementation/SKILL.md +1 -1
  24. package/dist/adapters/claude-bg.js +9 -7
  25. package/dist/adapters/grok-events.js +227 -0
  26. package/dist/adapters/grok-mcp-servidor.js +622 -0
  27. package/dist/adapters/grok-mcp.js +178 -0
  28. package/dist/adapters/grok.js +686 -0
  29. package/dist/agendador-cpu.js +92 -0
  30. package/dist/branch-de-estado.js +7 -2
  31. package/dist/changelog-check.js +217 -0
  32. package/dist/check-agil.js +289 -25
  33. package/dist/check-paralelo.js +73 -18
  34. package/dist/ci.js +3 -2
  35. package/dist/claim-lint.js +29 -0
  36. package/dist/claims.js +4 -0
  37. package/dist/conducao.js +5 -0
  38. package/dist/docs.js +16 -16
  39. package/dist/doctor-forja.js +1 -3
  40. package/dist/doctor.js +1 -1
  41. package/dist/entrega-pr.js +10 -10
  42. package/dist/fabrica-estado.js +20 -11
  43. package/dist/fila-pesada.js +121 -11
  44. package/dist/hitl-classificacao.js +1 -0
  45. package/dist/horario.js +4 -1
  46. package/dist/index.js +63 -35
  47. package/dist/integracoes-locais.js +2 -0
  48. package/dist/intelligence-graph-cli.js +27 -9
  49. package/dist/locale-accounts.js +11 -3
  50. package/dist/locale-ajuda.js +10 -6
  51. package/dist/locale-claims.js +16 -2
  52. package/dist/locale-forja.js +2 -4
  53. package/dist/locale-grafo.js +56 -1
  54. package/dist/locale-maestro.js +38 -0
  55. package/dist/locale-network.js +8 -6
  56. package/dist/locale-onboarding.js +14 -0
  57. package/dist/locale-operacao-restante.js +2 -2
  58. package/dist/locale-preflight.js +80 -0
  59. package/dist/locale-projetos.js +6 -0
  60. package/dist/locale-prompts.js +8 -8
  61. package/dist/locale-pulse.js +4 -0
  62. package/dist/locale-retry.js +56 -2
  63. package/dist/locale-security-scan.js +35 -1
  64. package/dist/locale-ship.js +10 -2
  65. package/dist/locale-verify.js +15 -0
  66. package/dist/locale.js +24 -7
  67. package/dist/maestro-cli.js +12 -17
  68. package/dist/manifest.js +6 -1
  69. package/dist/mcp-install.js +24 -13
  70. package/dist/mcp-server.js +62 -33
  71. package/dist/mcp-verify-async.js +353 -0
  72. package/dist/mcp-verify-worker.js +17 -0
  73. package/dist/mcp-verify.js +10 -1
  74. package/dist/merge-da-entrega.js +85 -0
  75. package/dist/network-roadmap.js +18 -14
  76. package/dist/noctum-aceite.js +154 -14
  77. package/dist/noctum-cli.js +69 -12
  78. package/dist/noctum-entrega.js +55 -7
  79. package/dist/noctum-envelope.js +1 -1
  80. package/dist/noctum-execucao.js +5 -1
  81. package/dist/noctum-fechamento.js +18 -7
  82. package/dist/noctum-fila.js +6 -1
  83. package/dist/noctum-guarda.js +81 -4
  84. package/dist/noctum-maquinas.js +5 -1
  85. package/dist/noctum-modo.js +35 -7
  86. package/dist/noctum-reconciliacao.js +4 -1
  87. package/dist/noctum-rede.js +102 -0
  88. package/dist/noctum-relatorio.js +44 -10
  89. package/dist/noctum-retomada-rede.js +71 -0
  90. package/dist/noctum-vigia.js +49 -9
  91. package/dist/parado-no-condutor.js +14 -15
  92. package/dist/pedido-conducao.js +51 -0
  93. package/dist/phase.js +43 -15
  94. package/dist/postura-local.js +7 -3
  95. package/dist/preflight.js +27 -27
  96. package/dist/prompts.js +4 -2
  97. package/dist/pulse.js +4 -2
  98. package/dist/ratelimit.js +1 -0
  99. package/dist/rede.js +24 -12
  100. package/dist/retry.js +115 -10
  101. package/dist/runtime-context.js +165 -45
  102. package/dist/runtime-escada.js +7 -1
  103. package/dist/runtime-profiles.js +27 -17
  104. package/dist/runtimes.js +42 -5
  105. package/dist/security-scan/agil.js +18 -7
  106. package/dist/security-scan/cli.js +2 -2
  107. package/dist/security-scan/executor.js +8 -0
  108. package/dist/security-scan/report.js +1 -1
  109. package/dist/session-watcher-grok.js +425 -0
  110. package/dist/session-watcher.js +52 -11
  111. package/dist/sessoes-contas.js +33 -5
  112. package/dist/setup.js +11 -11
  113. package/dist/varredura-acoes.js +8 -15
  114. package/dist/verify.js +191 -43
  115. package/package.json +2 -2
  116. package/skills/core/orkastery-bootstrap/SKILL.md +1 -1
  117. package/skills/phases/check-quality/SKILL.md +1 -1
  118. package/skills/phases/go-implementation/SKILL.md +1 -1
@@ -0,0 +1,686 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.IGNORADOS_DA_CONFIG_DO_FILHO = exports.MARCA_DA_CONFIG_DO_FILHO = exports.ESPERA_PADRAO_MS = exports.ESFORCO_DO_GROK = exports.PERFIL_DE_SANDBOX_PADRAO = exports.PERFIL_DE_SANDBOX_DO_GROK = exports.AMBIENTE_SEM_MCP_DE_OUTROS_CLIENTES = exports.ENVS_REMOVIDAS_DO_DESPACHO = exports.FONTE_VERIFICACAO = exports.NOME_ADAPTER = void 0;
37
+ exports.esforcoDoGrok = esforcoDoGrok;
38
+ exports.ferramentasDoGrok = ferramentasDoGrok;
39
+ exports.ferramentasServidasAoGrok = ferramentasServidasAoGrok;
40
+ exports.ambienteDoDespacho = ambienteDoDespacho;
41
+ exports.ambienteDoFilho = ambienteDoFilho;
42
+ exports.comandoDeLogin = comandoDeLogin;
43
+ exports.conferirAuth = conferirAuth;
44
+ exports.disponivel = disponivel;
45
+ exports.versao = versao;
46
+ exports.casaDoGrok = casaDoGrok;
47
+ exports.perfilDeSandbox = perfilDeSandbox;
48
+ exports.montarComando = montarComando;
49
+ exports.configDoFilho = configDoFilho;
50
+ exports.gravarConfigDaWorktree = gravarConfigDaWorktree;
51
+ exports.montarComandoDeRetomada = montarComandoDeRetomada;
52
+ exports.acharSessaoGrok = acharSessaoGrok;
53
+ exports.consultarSessoesGrok = consultarSessoesGrok;
54
+ exports.despachar = despachar;
55
+ /**
56
+ * RM-067 (F1 da ork-grokbuild): runtime `grok`, o Grok Build headless da xAI. Nasceu só para despacho explícito
57
+ * (`ork phase run --runtime grok`, D1); desde a F4, é runtime de bloco no setup, nunca o padrão do projeto, e desde a F4b
58
+ * leva o MCP do `ork` pela config que o despacho grava na worktree (`gravarConfigDaWorktree`). Desde a F4c, o servidor
59
+ * dessa config é um processo do `ork`, fora do sandbox do `grok` (`grok-mcp.ts`), e o `grok` se conecta a ele por `url`, com o
60
+ * token do despacho só no ambiente (`bearer_token_env_var`).
61
+ *
62
+ * O despacho roda `grok -p <prompt> --output-format streaming-json -s <id>` desanexado, pelo supervisor do `codex-runner`
63
+ * (D6): o log do stream, o `.process.json` com a identidade do filho e o `.exit.json` com o código e o sinal do `close`
64
+ * real. O id da sessão é escolhido pelo `ork` (D5), e a prova de que ela existe é a pasta
65
+ * `<GROK_HOME>/sessions/<grupo>/<id>/` com o `updates.jsonl`, que o próprio runtime grava. O `ork` só lista nomes e lê o
66
+ * tamanho e o mtime desse arquivo, nunca o conteúdo, e nunca abre o `auth.json`. O fim da sessão é do watcher
67
+ * (`session-watcher-grok.ts`), pelo `end` do stream e pelo recibo do supervisor.
68
+ */
69
+ const fs = __importStar(require("node:fs"));
70
+ const os = __importStar(require("node:os"));
71
+ const path = __importStar(require("node:path"));
72
+ const node_crypto_1 = require("node:crypto");
73
+ const smol_toml_1 = require("smol-toml");
74
+ const runtime_ambiente_1 = require("../runtime-ambiente");
75
+ const runtime_context_1 = require("../runtime-context");
76
+ const runtime_profiles_1 = require("../runtime-profiles");
77
+ const util_1 = require("../util");
78
+ const claude_bg_1 = require("./claude-bg");
79
+ const codex_runner_1 = require("./codex-runner");
80
+ const grok_events_1 = require("./grok-events");
81
+ const grok_mcp_1 = require("./grok-mcp");
82
+ const mcp_grafo_1 = require("../mcp-grafo");
83
+ exports.NOME_ADAPTER = 'grok';
84
+ /** De onde vem a prova de que a sessão existe (vai ao `phase_dispatch_verified`). */
85
+ exports.FONTE_VERIFICACAO = 'pasta da sessao em $GROK_HOME/sessions';
86
+ /**
87
+ * D4: o ambiente do filho é o da assinatura, sem as variáveis de provider pago e sem `XAI_API_KEY`. A D1 da RM-067 foi
88
+ * decidida pelo dono em 27/10/2026 pela alternativa b (a assinatura agora): o filho autentica pelo login que o próprio
89
+ * `grok` guardou no `GROK_HOME` do perfil, nunca pela chave. Tirar a variável do ambiente não lê nem copia a credencial.
90
+ * RM-067 (F4c, GO-FIX 2, R3): o token do servidor do `ork` herdado de uma sessão (o `ork` rodado no shell dela) também sai;
91
+ * o despacho com o MCP põe o token dele por cima.
92
+ */
93
+ exports.ENVS_REMOVIDAS_DO_DESPACHO = [...runtime_ambiente_1.ENVS_DE_PROVIDER_PAGO, 'XAI_API_KEY', grok_mcp_1.ENV_DO_TOKEN_DO_SERVIDOR_GROK];
94
+ /**
95
+ * RM-067 (F4b, G4 e D5): o filho do `grok` nunca carrega o MCP de outros clientes. Por padrão, o `grok` lê a config de MCP
96
+ * do Claude e a do Cursor (G16); as duas variáveis desligam essa leitura (`false` ou `0`, G5), com ou sem o MCP do `ork`.
97
+ */
98
+ exports.AMBIENTE_SEM_MCP_DE_OUTROS_CLIENTES = Object.freeze({
99
+ GROK_CLAUDE_MCPS_ENABLED: '0', GROK_CURSOR_MCPS_ENABLED: '0',
100
+ });
101
+ /**
102
+ * D3 e F4 (E7 da RM-067): a postura de sandbox do manifesto (vocabulário do Codex) no perfil do `grok` (`--sandbox`).
103
+ * `workspace` é o piso, o perfil de toda postura que não afrouxa (o `grok` usa Landlock, não o bubblewrap do Codex). A
104
+ * postura mais frouxa (`danger-full-access`) vira `off`, sem sandbox, e só despacha com `ork setup sandbox confirmar`
105
+ * nesta máquina: sem a confirmação, o `ork phase run` e o `ork retry` recusam com `runtime.sandbox-nao-confirmado`
106
+ * (`postura-local.ts`) antes de montar o comando. O `strict` do `grok` não tem postura do Orkastery que o peça. Nenhuma
107
+ * postura aprova tudo: nunca `--yolo`, `--always-approve` nem `--permission-mode bypassPermissions`.
108
+ */
109
+ exports.PERFIL_DE_SANDBOX_DO_GROK = Object.freeze({
110
+ 'read-only': 'read-only',
111
+ 'workspace-write': 'workspace',
112
+ 'danger-full-access': 'off',
113
+ });
114
+ exports.PERFIL_DE_SANDBOX_PADRAO = 'workspace';
115
+ /**
116
+ * RM-067 (F4, E6): o esforço do Orkastery nos níveis canônicos do `grok` (`none`, `minimal`, `low`, `medium`, `high`,
117
+ * `xhigh`, `max`, no guia headless). O `eco` vira `minimal`, para a escada de esforço (`eco` antes de `low`) seguir com
118
+ * degraus distintos; os outros cinco têm o mesmo nome. Cada modelo só aceita os níveis que o próprio menu anuncia, e esse
119
+ * menu só o runtime conhece (`grok models`): o nível que o modelo não anuncia é recusado pelo próprio `grok` no despacho.
120
+ * Valor fora da tabela vai como veio, porque o `grok` também aceita o id de uma opção do menu do modelo.
121
+ */
122
+ exports.ESFORCO_DO_GROK = Object.freeze({
123
+ eco: 'minimal', low: 'low', medium: 'medium', high: 'high', xhigh: 'xhigh', max: 'max',
124
+ });
125
+ /** O nível do `--effort` do `grok` para o esforço do Orkastery (E6). */
126
+ function esforcoDoGrok(effort) {
127
+ const chave = effort.trim().toLowerCase();
128
+ return Object.hasOwn(exports.ESFORCO_DO_GROK, chave) ? exports.ESFORCO_DO_GROK[chave] : effort.trim();
129
+ }
130
+ /** O prefixo das ferramentas do servidor `orkastery` no `--allowedTools` do `claude-bg`. */
131
+ const PREFIXO_DO_CLAUDE = 'mcp__orkastery__';
132
+ /**
133
+ * RM-067 (F4b, G2 e D1): as ferramentas do MCP do `ork` que um despacho do `grok` libera, as mesmas do `--allowedTools` do
134
+ * `claude-bg` no mesmo caso, sem o prefixo: no PLAN que fecha o bloco, as consultas, `ork_artifact_write` e
135
+ * `ork_decision_record`, em qualquer perfil; fora dele, as consultas e, no perfil `worktree`, `ork_git_status` e as sete
136
+ * mutações. As consultas do grafo entram com a flag do manifesto da raiz. Montada na chamada, como no `claude-bg`: no
137
+ * ciclo de imports, o valor importado pode ainda não existir quando este módulo carrega.
138
+ */
139
+ function ferramentasDoGrok(perfil, plano, grafo) {
140
+ const semPrefixo = (nomes) => nomes.map((n) => n.slice(PREFIXO_DO_CLAUDE.length));
141
+ const consultas = [...semPrefixo(claude_bg_1.CONSULTAS_MCP), ...(grafo ? mcp_grafo_1.TOOLS_DO_GRAFO : [])];
142
+ if (plano)
143
+ return [...consultas, 'ork_artifact_write', 'ork_decision_record'];
144
+ return perfil === 'worktree' ? [...consultas, 'ork_git_status', ...semPrefixo(claude_bg_1.MUTACOES_WORKTREE)] : consultas;
145
+ }
146
+ /**
147
+ * RM-067 (F4b, G1): o que o servidor `--host grok` lista, para o perfil filho da instalação: a união dos dois casos de
148
+ * `ferramentasDoGrok` (o PLAN e o resto do bloco). O headless não aprova ferramenta em diálogo, então o servidor não lista
149
+ * nada que o `--allow` de algum despacho desse perfil não libere.
150
+ */
151
+ function ferramentasServidasAoGrok(perfil, grafo) {
152
+ return [...new Set([...ferramentasDoGrok(perfil, false, grafo), ...ferramentasDoGrok(perfil, true, grafo)])];
153
+ }
154
+ /** D6: quanto o despacho espera a pasta da sessão ou o recibo do supervisor. */
155
+ exports.ESPERA_PADRAO_MS = 60_000;
156
+ const ID_DE_SESSAO = /^[0-9a-f]{8}(?:-[0-9a-f]{4}){3}-[0-9a-f]{12}$/i;
157
+ const LIMITE_METADADO_BYTES = 65536;
158
+ const LIMITE_STDERR_BYTES = 4096;
159
+ /** O stderr do resultado: o despacho grava os primeiros 500 caracteres dele no ledger. */
160
+ const LIMITE_STDERR_NO_RESULTADO = 500;
161
+ /** D4: o ambiente do despacho, sem provider pago e sem `XAI_API_KEY`; o `GROK_HOME` do processo fica. */
162
+ function ambienteDoDespacho(base = process.env) {
163
+ const env = (0, runtime_ambiente_1.ambienteDeAssinatura)(base);
164
+ for (const nome of exports.ENVS_REMOVIDAS_DO_DESPACHO)
165
+ delete env[nome];
166
+ return env;
167
+ }
168
+ /**
169
+ * O ambiente do filho: o do despacho, com o `GROK_HOME` do perfil (F3) e o contexto da condução por cima. Perfil de outro
170
+ * runtime, ou sem `grokHome`, recusa: cair para a casa do processo seria despachar pela conta errada. RM-067 (F4b): as
171
+ * duas variáveis que desligam o MCP de outros clientes vão por último, para nada as desfazer.
172
+ */
173
+ function ambienteDoFilho(pedido, base = process.env) {
174
+ if (pedido.perfil && (pedido.perfil.runtime !== 'grok' || !pedido.perfil.grokHome))
175
+ throw new Error('runtime.profiles.invalid: perfil sem grokHome no despacho grok');
176
+ return { ...(0, runtime_profiles_1.ambienteComPerfil)(ambienteDoDespacho(base), pedido.perfil), ...(pedido.ambienteExtra ?? {}),
177
+ ...exports.AMBIENTE_SEM_MCP_DE_OUTROS_CLIENTES };
178
+ }
179
+ /**
180
+ * RM-067 (F3): o login do perfil é sempre do próprio CLI, com o `GROK_HOME` do perfil: `grok login` pelo navegador ou
181
+ * `grok login --device-auth` pelo código de dispositivo (G3, G4 da página). O `ork` nunca abre o `auth.json` que o
182
+ * `grok` grava.
183
+ */
184
+ function comandoDeLogin(codigoDeDispositivo = false) {
185
+ return ['grok', 'login', ...(codigoDeDispositivo ? ['--device-auth'] : [])];
186
+ }
187
+ /**
188
+ * RM-067 (F3): a conferência de login do perfil. O `grok` não documenta um comando de status do login (fontes da página),
189
+ * e o `ork` nunca lê o `auth.json`: a conferência fica sem medida (`semConferencia`), o perfil segue no rodízio e quem
190
+ * prova o login é o próprio despacho, cujo erro de login do runtime vira `runtime.auth-missing` e tira o perfil.
191
+ */
192
+ function conferirAuth(perfil) {
193
+ try {
194
+ ambienteDoFilho({ perfil: perfil ?? undefined });
195
+ }
196
+ catch (e) {
197
+ return { ok: false, detalhe: e.message };
198
+ }
199
+ return { ok: true, semConferencia: true,
200
+ detalhe: 'o grok nao documenta conferencia de login; o despacho prova o login (erro de login do runtime vira runtime.auth-missing)' };
201
+ }
202
+ /** O runtime está disponível? No PATH do ambiente informado (o do filho, no despacho). */
203
+ function disponivel(env = process.env) {
204
+ return (0, util_1.noPath)('grok', env);
205
+ }
206
+ /** Versão do runtime, para quem lista os runtimes. */
207
+ function versao() {
208
+ const r = (0, util_1.exec)('grok', ['--version'], undefined, 30000, ambienteDoDespacho());
209
+ return r.ok ? r.stdout.trim() : null;
210
+ }
211
+ /** A casa do `grok`: o `GROK_HOME` do ambiente ou `~/.grok`, como o guia de sessões diz. */
212
+ function casaDoGrok(env = process.env) {
213
+ const valor = (env.GROK_HOME ?? '').trim();
214
+ return valor !== '' ? valor : path.join(os.homedir(), '.grok');
215
+ }
216
+ /** D3: o perfil de sandbox do `grok` para a postura; fora da tabela, recusa tipada. */
217
+ function perfilDeSandbox(sandbox, colaboracao) {
218
+ if (colaboracao === 'plan')
219
+ return 'read-only';
220
+ if (sandbox === undefined)
221
+ return exports.PERFIL_DE_SANDBOX_PADRAO;
222
+ if (!Object.hasOwn(exports.PERFIL_DE_SANDBOX_DO_GROK, sandbox)) {
223
+ throw new Error(`runtime.unavailable: sandbox ${sandbox} sem perfil do grok`);
224
+ }
225
+ return exports.PERFIL_DE_SANDBOX_DO_GROK[sandbox];
226
+ }
227
+ /** O contexto do MCP do despacho, revalidado aqui como nos outros adaptadores; sem ele, `null`. */
228
+ function contextoDoDespacho(pedido) {
229
+ if (!pedido.contextoRuntime)
230
+ return null;
231
+ if (pedido.vinculo && pedido.contextoRuntime.threadId !== pedido.vinculo.thread)
232
+ throw new Error('runtime.context.scope: thread do contexto difere do despacho');
233
+ return (0, runtime_context_1.validarContextoDoGrok)(pedido.contextoRuntime, pedido.cwd);
234
+ }
235
+ /** RM-067 (F4b, G2; F4c, D4): as ferramentas que o despacho libera, a mesma lista para o `--allow` e para o servidor do `ork`. */
236
+ function ferramentasDoDespacho(pedido, contexto) {
237
+ return contexto ? ferramentasDoGrok(contexto.permissoesFilho, pedido.colaboracao === 'plan', contexto.grafoMcp) : [];
238
+ }
239
+ function comandoDoDespacho(pedido, ferramentas) {
240
+ const args = ['-p', pedido.prompt, '--output-format', 'streaming-json'];
241
+ if (pedido.sessionId)
242
+ args.push('-s', pedido.sessionId);
243
+ args.push('--cwd', pedido.cwd, '--trust');
244
+ if (pedido.model)
245
+ args.push('-m', pedido.model);
246
+ if (pedido.effort?.trim())
247
+ args.push('--effort', esforcoDoGrok(pedido.effort));
248
+ args.push('--sandbox', perfilDeSandbox(pedido.sandbox, pedido.colaboracao), '--no-auto-update');
249
+ // RM-067 (F4b, G2): com o MCP do ork, uma regra por ferramenta liberada, no fim, para o despacho sem contexto ficar igual
250
+ // e a retomada (que guarda o que vem depois do `-s`) levar as mesmas regras.
251
+ for (const ferramenta of ferramentas)
252
+ args.push('--allow', `MCPTool(orkastery__${ferramenta})`);
253
+ return ['grok', ...args];
254
+ }
255
+ /**
256
+ * D2: a linha de comando exata do despacho (usada também pelo `--dry-run`). O prompt fica no argumento 2, que o ledger
257
+ * mascara. Nunca `--yolo`, `--always-approve` nem `--permission-mode`.
258
+ *
259
+ * RM-067 (F4, E8): `--trust` confia a pasta do `--cwd`, a worktree da thread, que é o checkout do próprio projeto. O guia
260
+ * de regras de projeto do `grok` diz que o `AGENTS.md` só carrega na partida com a pasta confiada (`--trust` ou a
261
+ * concessão interativa, que o headless não tem), e o de permissões diz que a partida headless com instruções do projeto
262
+ * exige `--trust`. A confiança também liga, nessa pasta, as regras de permissão do projeto e os hooks de projeto que o
263
+ * `grok` lê; não aprova ferramenta nenhuma.
264
+ *
265
+ * RM-067 (F4b, G2): num projeto preparado, o comando leva no fim um `--allow MCPTool(orkastery__<ferramenta>)` por
266
+ * ferramenta do MCP do `ork` que o caso libera (`ferramentasDoGrok`), e nenhuma outra regra.
267
+ */
268
+ function montarComando(pedido) {
269
+ return comandoDoDespacho(pedido, ferramentasDoDespacho(pedido, contextoDoDespacho(pedido)));
270
+ }
271
+ /** RM-067 (F4b, G3): a primeira linha da config que o `ork` grava na worktree; sem ela, o arquivo não é do `ork`. */
272
+ exports.MARCA_DA_CONFIG_DO_FILHO = '# Gerado pelo ork para a sessao filha do grok; o ork reescreve este arquivo a cada despacho.';
273
+ /**
274
+ * RM-067 (F4b, GO-FIX A2): o `.gitignore` da pasta `.grok` só esconde o que o `ork` grava nela, pelo nome. Regra ou arquivo
275
+ * novo que a sessão deixe em `.grok/` (o `grok` lê `.grok/rules/` na partida) segue visível ao git e à prova do GO.
276
+ */
277
+ exports.IGNORADOS_DA_CONFIG_DO_FILHO = '/config.toml\n/.gitignore\n/.ork-*.tmp\n';
278
+ const LIMITE_DA_CONFIG_BYTES = 65536;
279
+ /** D1 da F4c: a única URL que a config da worktree aceita, a do servidor do `ork` no loopback. */
280
+ const URL_DO_SERVIDOR_DO_ORK = /^http:\/\/127\.0\.0\.1:[1-9][0-9]{0,4}\/mcp$/;
281
+ /** D2 da F4c: o nome da variável do token, sem nada que o `grok` expandisse. */
282
+ const NOME_DE_VARIAVEL = /^[A-Z_][A-Z0-9_]{0,63}$/;
283
+ /**
284
+ * RM-067 (F4b, G3 e G7; F4c, D7): o `.grok/config.toml` da worktree, com o servidor do `ork` vinculado à thread e ao
285
+ * despacho. Desde a F4c, a entrada é a forma `url` do guia oficial (G16): a URL do servidor do `ork` no loopback e o
286
+ * `bearer_token_env_var` (G5), o nome da variável com o token, que o despacho põe só no ambiente do `grok`; nada de
287
+ * `command`, `args` ou `env`, porque o servidor não é mais filho do `grok`, e nada de token em arquivo, que a sessão de outra
288
+ * thread leria (GO-FIX 1, achado 1). O `grok` expande `${VAR}` na `url` (G16): valor com `$` recusa, porque o servidor
289
+ * alcançado seria outro. O texto volta pelo parser de TOML e o documento inteiro tem de ser exatamente a entrada pedida
290
+ * (GO-FIX S2).
291
+ */
292
+ function configDoFilho(conexao, threadId) {
293
+ if (conexao.url.includes('$'))
294
+ throw new Error('runtime.context.conflict: url do servidor do ork tem $, que o grok expandiria; use uma url sem $');
295
+ if (!URL_DO_SERVIDOR_DO_ORK.test(conexao.url))
296
+ throw new Error('runtime.context.conflict: a url do servidor do ork nao e a do loopback local (http://127.0.0.1:<porta>/mcp)');
297
+ if (!NOME_DE_VARIAVEL.test(conexao.bearerTokenEnvVar))
298
+ throw new Error('runtime.context.conflict: o bearer_token_env_var do servidor do ork nao e um nome de variavel');
299
+ const texto = [exports.MARCA_DA_CONFIG_DO_FILHO, `# thread ${threadId}`, '[mcp_servers.orkastery]',
300
+ `url = ${JSON.stringify(conexao.url)}`, `bearer_token_env_var = ${JSON.stringify(conexao.bearerTokenEnvVar)}`].join('\n') + '\n';
301
+ let lido;
302
+ try {
303
+ lido = JSON.stringify((0, smol_toml_1.parse)(texto));
304
+ }
305
+ catch {
306
+ throw new Error('runtime.context.conflict: a config do servidor do ork nao sai como TOML valido');
307
+ }
308
+ const esperado = JSON.stringify({ mcp_servers: { orkastery: { url: conexao.url, bearer_token_env_var: conexao.bearerTokenEnvVar } } });
309
+ if (lido !== esperado)
310
+ throw new Error('runtime.context.conflict: a config do servidor do ork nao volta igual pelo TOML');
311
+ return texto;
312
+ }
313
+ /** O texto de um arquivo comum e pequeno, sem seguir link; ausente vira `null`, e o resto recusa. */
314
+ function textoDaWorktree(arquivo) {
315
+ let fd;
316
+ try {
317
+ fd = fs.openSync(arquivo, fs.constants.O_RDONLY | fs.constants.O_NOFOLLOW | fs.constants.O_NONBLOCK);
318
+ }
319
+ catch (e) {
320
+ if (e.code === 'ENOENT')
321
+ return null;
322
+ throw new Error(`runtime.context.conflict: .grok/${path.basename(arquivo)} da worktree e link ou ilegivel; o ork nao escreve por cima`);
323
+ }
324
+ try {
325
+ const st = fs.fstatSync(fd);
326
+ if (!st.isFile() || st.size > LIMITE_DA_CONFIG_BYTES)
327
+ throw new Error(`runtime.context.conflict: .grok/${path.basename(arquivo)} da worktree nao e arquivo comum pequeno`);
328
+ return fs.readFileSync(fd, 'utf8');
329
+ }
330
+ finally {
331
+ fs.closeSync(fd);
332
+ }
333
+ }
334
+ /** Grava um arquivo pequeno por temporário e `rename`, na mesma pasta. */
335
+ function gravarNaWorktree(arquivo, texto) {
336
+ const temp = path.join(path.dirname(arquivo), `.ork-${(0, node_crypto_1.randomUUID)()}.tmp`);
337
+ try {
338
+ fs.writeFileSync(temp, texto, { flag: 'wx', mode: 0o600 });
339
+ fs.renameSync(temp, arquivo);
340
+ }
341
+ finally {
342
+ fs.rmSync(temp, { force: true });
343
+ }
344
+ }
345
+ /**
346
+ * RM-067 (F4b, G3 e D3): grava a config da sessão filha na worktree, fora do git e sem apagar arquivo de ninguém. Recusa
347
+ * antes de escrever: `.grok` que é link ou não é pasta, `config.toml` ou `.gitignore` versionados, link no lugar de
348
+ * arquivo e config sem a marca do `ork`. O `.gitignore` com os nomes dos arquivos do `ork` só nasce quando falta (nunca
349
+ * reescreve o de outro), e o `git status` dos dois arquivos tem de sair vazio depois; se não sair, desfaz o que gravou e
350
+ * recusa.
351
+ */
352
+ function gravarConfigDaWorktree(cwd, texto) {
353
+ const pasta = path.join(cwd, '.grok'), arquivo = path.join(pasta, 'config.toml'), ignorar = path.join(pasta, '.gitignore');
354
+ const st = fs.lstatSync(pasta, { throwIfNoEntry: false });
355
+ if (st && (st.isSymbolicLink() || !st.isDirectory()))
356
+ throw new Error('runtime.context.worktree: .grok da worktree e link ou nao e pasta; o ork nao grava a config da sessao por ele');
357
+ const git = (args) => {
358
+ const r = (0, util_1.exec)('git', args, cwd, 30000);
359
+ if (!r.ok)
360
+ throw new Error(`runtime.context.conflict: git ${args[0]} da worktree falhou`);
361
+ return r.stdout.trim();
362
+ };
363
+ const versionados = git(['ls-files', '--', '.grok/config.toml', '.grok/.gitignore']);
364
+ if (versionados) {
365
+ throw new Error(`runtime.context.conflict: ${versionados.split('\n').join(', ')} versionado na worktree; ` +
366
+ 'o ork grava o servidor da sessao num arquivo fora do git');
367
+ }
368
+ const anterior = textoDaWorktree(arquivo);
369
+ if (anterior !== null && !anterior.startsWith(`${exports.MARCA_DA_CONFIG_DO_FILHO}\n`))
370
+ throw new Error('runtime.context.conflict: .grok/config.toml da worktree nao e do ork; mova o arquivo para o ork gravar o servidor da sessao');
371
+ const ignorarAntes = textoDaWorktree(ignorar);
372
+ if (!st)
373
+ fs.mkdirSync(pasta, { mode: 0o700 });
374
+ try {
375
+ if (ignorarAntes === null)
376
+ gravarNaWorktree(ignorar, exports.IGNORADOS_DA_CONFIG_DO_FILHO);
377
+ gravarNaWorktree(arquivo, texto);
378
+ if (git(['status', '--porcelain', '--untracked-files=all', '--', '.grok/config.toml', '.grok/.gitignore']))
379
+ throw new Error('runtime.context.conflict: a config da sessao ficaria visivel ao git na worktree; confira o .gitignore de .grok');
380
+ }
381
+ catch (e) {
382
+ // Desfaz só o que este despacho gravou: a config anterior do ork volta, e o que nasceu agora sai.
383
+ if (anterior !== null)
384
+ gravarNaWorktree(arquivo, anterior);
385
+ else
386
+ fs.rmSync(arquivo, { force: true });
387
+ if (ignorarAntes === null)
388
+ fs.rmSync(ignorar, { force: true });
389
+ if (!st) {
390
+ try {
391
+ fs.rmdirSync(pasta);
392
+ }
393
+ catch { /* sobrou arquivo que não é deste despacho */ }
394
+ }
395
+ throw e;
396
+ }
397
+ }
398
+ /**
399
+ * D11: a retomada de uma sessão interrompida, derivada do comando do despacho (o do ledger tem o prompt mascarado): o
400
+ * argumento 2 vira o texto da retomada e `-s <id>` vira `-r <id>`; o resto (cwd, modelo, esforço, sandbox) fica. O guia
401
+ * diz que a sessão interrompida guarda o estado até a última ferramenta concluída. Comando que não é do `grok`, ou sem
402
+ * `-s <id>`, devolve `null`.
403
+ */
404
+ function montarComandoDeRetomada(comando, sessionId, texto = 'continue') {
405
+ if (comando[0] !== 'grok' || comando[1] !== '-p' || comando.length < 3)
406
+ return null;
407
+ const i = comando.indexOf('-s', 3);
408
+ if (i < 0 || comando[i + 1] !== sessionId)
409
+ return null;
410
+ return [...comando.slice(0, 2), texto, ...comando.slice(3, i), '-r', sessionId, ...comando.slice(i + 2)];
411
+ }
412
+ /**
413
+ * D5: a pasta `<casa>/sessions/<grupo>/<id>/` com `updates.jsonl` arquivo comum. A codificação do cwd no nome do grupo
414
+ * não é documentada, então a busca olha o id um nível abaixo de `sessions/`, sem seguir link. Exatamente um grupo com a
415
+ * sessão, ou `null`: o id que aparece em dois grupos não prova qual é a sessão despachada.
416
+ */
417
+ function acharSessaoGrok(sessionId, casa = casaDoGrok()) {
418
+ if (!ID_DE_SESSAO.test(sessionId))
419
+ return null;
420
+ const raiz = path.join(casa, 'sessions');
421
+ let grupos;
422
+ try {
423
+ if (!fs.lstatSync(raiz).isDirectory())
424
+ return null;
425
+ grupos = fs.readdirSync(raiz, { withFileTypes: true });
426
+ }
427
+ catch {
428
+ return null;
429
+ }
430
+ const achadas = [];
431
+ for (const grupo of grupos) {
432
+ if (!grupo.isDirectory())
433
+ continue;
434
+ const dir = path.join(raiz, grupo.name, sessionId);
435
+ try {
436
+ if (!fs.lstatSync(dir).isDirectory())
437
+ continue;
438
+ const updates = path.join(dir, 'updates.jsonl');
439
+ const st = fs.lstatSync(updates);
440
+ if (st.isFile())
441
+ achadas.push({ dir, updates, tamanho: st.size, mtimeMs: st.mtimeMs });
442
+ }
443
+ catch { /* o grupo não tem a sessão */ }
444
+ }
445
+ return achadas.length === 1 ? achadas[0] : null;
446
+ }
447
+ /**
448
+ * RM-067 (F4): as sessões de uma casa do `grok` para o inventário (`ork sessions`), pela fonte que o próprio runtime grava:
449
+ * cada pasta `<casa>/sessions/<grupo>/<id>/` com o `updates.jsonl`. Como na prova de existência (D5), o `ork` lista nomes
450
+ * e lê o mtime e o tamanho do arquivo, nunca o conteúdo; a atividade é o mtime. O grupo é o cwd codificado, cuja
451
+ * codificação o guia não documenta, então o cwd da sessão fica sem valor. Link e entrada estranha deixam a consulta
452
+ * parcial (`ok: false`), nunca "zero sessões".
453
+ */
454
+ function consultarSessoesGrok(casa = casaDoGrok()) {
455
+ const raiz = path.join(casa, 'sessions');
456
+ let grupos;
457
+ try {
458
+ const st = fs.lstatSync(raiz);
459
+ if (!st.isDirectory())
460
+ return { ok: false, ausente: false, sessoes: [], detalhe: 'sessions nao e pasta comum' };
461
+ grupos = fs.readdirSync(raiz, { withFileTypes: true });
462
+ }
463
+ catch (e) {
464
+ if (e.code === 'ENOENT')
465
+ return { ok: true, ausente: true, sessoes: [], detalhe: 'sem a pasta sessions' };
466
+ return { ok: false, ausente: false, sessoes: [], detalhe: 'pasta sessions inacessivel' };
467
+ }
468
+ const sessoes = [];
469
+ let parcial = false;
470
+ for (const grupo of grupos) {
471
+ if (!grupo.isDirectory()) {
472
+ parcial ||= grupo.isSymbolicLink();
473
+ continue;
474
+ }
475
+ let ids;
476
+ try {
477
+ ids = fs.readdirSync(path.join(raiz, grupo.name), { withFileTypes: true });
478
+ }
479
+ catch {
480
+ parcial = true;
481
+ continue;
482
+ }
483
+ for (const id of ids) {
484
+ if (!ID_DE_SESSAO.test(id.name))
485
+ continue;
486
+ if (!id.isDirectory()) {
487
+ parcial ||= id.isSymbolicLink();
488
+ continue;
489
+ }
490
+ try {
491
+ const st = fs.lstatSync(path.join(raiz, grupo.name, id.name, 'updates.jsonl'));
492
+ if (st.isFile())
493
+ sessoes.push({ sessionId: id.name, kind: 'grok', atividadeEm: st.mtimeMs });
494
+ else
495
+ parcial = true;
496
+ }
497
+ catch (e) {
498
+ if (e.code !== 'ENOENT')
499
+ parcial = true;
500
+ }
501
+ }
502
+ }
503
+ return { ok: !parcial, ausente: false, sessoes,
504
+ detalhe: parcial ? 'inventario parcial; a pasta sessions tem link ou entrada ilegivel' : '' };
505
+ }
506
+ /** JSON pequeno de metadado do supervisor, sem seguir link; ausente ou ilegível vira `null`. */
507
+ function lerMetadado(arquivo) {
508
+ let fd;
509
+ try {
510
+ fd = fs.openSync(arquivo, fs.constants.O_RDONLY | fs.constants.O_NOFOLLOW | fs.constants.O_NONBLOCK);
511
+ }
512
+ catch {
513
+ return null;
514
+ }
515
+ try {
516
+ const st = fs.fstatSync(fd);
517
+ if (!st.isFile() || st.size > LIMITE_METADADO_BYTES)
518
+ return null;
519
+ const bytes = Buffer.alloc(st.size);
520
+ fs.readSync(fd, bytes, 0, st.size, 0);
521
+ return JSON.parse(bytes.toString('utf8'));
522
+ }
523
+ catch {
524
+ return null;
525
+ }
526
+ finally {
527
+ fs.closeSync(fd);
528
+ }
529
+ }
530
+ /**
531
+ * A cauda do stderr do filho, redigida e sem corte; ausente vira vazio. GO-FIX 1 (A4): a janela começa no meio de uma
532
+ * linha, e esse pedaço sai inteiro antes da redação, para nenhum segredo cortado perder o prefixo que a redação reconhece.
533
+ */
534
+ function caudaDoStderr(arquivo) {
535
+ let fd;
536
+ try {
537
+ fd = fs.openSync(arquivo, fs.constants.O_RDONLY | fs.constants.O_NOFOLLOW | fs.constants.O_NONBLOCK);
538
+ }
539
+ catch {
540
+ return '';
541
+ }
542
+ try {
543
+ const st = fs.fstatSync(fd);
544
+ if (!st.isFile())
545
+ return '';
546
+ const n = Math.min(st.size, LIMITE_STDERR_BYTES), bytes = Buffer.alloc(n);
547
+ fs.readSync(fd, bytes, 0, n, st.size - n);
548
+ let texto = bytes.toString('utf8');
549
+ if (st.size > n) {
550
+ const quebra = texto.indexOf('\n');
551
+ texto = quebra < 0 ? '' : texto.slice(quebra + 1);
552
+ }
553
+ return (0, grok_events_1.redigirTextoGrok)(texto);
554
+ }
555
+ finally {
556
+ fs.closeSync(fd);
557
+ }
558
+ }
559
+ /**
560
+ * GO-FIX 2 (N6): o fim do texto em até `limite` unidades UTF-16, a mesma unidade do `slice` de quem grava no ledger, que
561
+ * assim não tira nada; o corte não começa na metade baixa de um par substituto.
562
+ */
563
+ function fimDoTexto(texto, limite) {
564
+ if (texto.length <= limite)
565
+ return texto;
566
+ const fim = texto.slice(-limite);
567
+ const primeira = fim.charCodeAt(0);
568
+ return primeira >= 0xdc00 && primeira <= 0xdfff ? fim.slice(1) : fim;
569
+ }
570
+ const pausa = new Int32Array(new SharedArrayBuffer(4));
571
+ /**
572
+ * D6: despacha a fase e confere no disco do runtime que a sessão existe. Nunca self-report: o id é do `ork`, a pasta é
573
+ * do `grok`, e a falha antes da sessão vem do recibo do supervisor e do objeto `error` do stream.
574
+ */
575
+ function despachar(pedido) {
576
+ const sessionId = pedido.sessionId ?? (0, node_crypto_1.randomUUID)();
577
+ const falha = (erro, extra = {}) => ({ ok: false, comando: [], sessionId: null, verificada: false, stdout: '', stderr: '', erro, ...extra });
578
+ let comando;
579
+ let ambiente;
580
+ let contexto;
581
+ try {
582
+ contexto = contextoDoDespacho(pedido);
583
+ }
584
+ catch (e) {
585
+ return falha(`runtime.unavailable: ${e.message}`);
586
+ }
587
+ const ferramentas = ferramentasDoDespacho(pedido, contexto);
588
+ try {
589
+ comando = comandoDoDespacho({ ...pedido, sessionId }, ferramentas);
590
+ ambiente = ambienteDoFilho(pedido);
591
+ }
592
+ catch (e) {
593
+ return falha(e.message);
594
+ }
595
+ if (pedido.dryRun)
596
+ return { ok: true, comando, sessionId: null, verificada: false, stdout: '', stderr: '' };
597
+ if (!disponivel(ambiente))
598
+ return falha('runtime.unavailable: binario grok nao encontrado no PATH', { comando });
599
+ // O log nunca vai para a worktree: a prova do GO exige a worktree limpa.
600
+ if (!pedido.logDir || !path.isAbsolute(pedido.logDir)) {
601
+ return falha('runtime.unavailable: despacho grok sem o diretorio de logs da thread', { comando });
602
+ }
603
+ const logPath = path.join(pedido.logDir, `${sessionId}.grok.jsonl`);
604
+ // RM-067 (F4c, D6 e D7): com o MCP, o servidor do ork sobe antes de tudo, fora do sandbox do grok, com as ferramentas do
605
+ // --allow deste despacho; a config da worktree aponta para ele e vai antes do supervisor, porque o grok a lê na partida,
606
+ // com a pasta confiada (F4b, G3). O token vai só ao ambiente do grok (D2). Toda falha depois de o servidor subir o encerra.
607
+ let servidor = null;
608
+ if (contexto) {
609
+ const especificacao = contexto.servidor;
610
+ if (!especificacao.dispatchId)
611
+ return falha('runtime.unavailable: runtime.context.identity: o grok com o MCP do ork exige a identidade do despacho', { comando });
612
+ try {
613
+ servidor = (0, grok_mcp_1.iniciarServidorDoGrok)({ projeto: especificacao.projeto, threadId: especificacao.threadId,
614
+ dispatchId: especificacao.dispatchId, permissoesFilho: especificacao.permissoesFilho, transporteShip: especificacao.transporteShip,
615
+ ferramentas, logPath, conferirProcesso: perfilDeSandbox(pedido.sandbox, pedido.colaboracao) !== 'off' }, pedido.cwd, { ...ambiente, ...(especificacao.env ?? {}) });
616
+ }
617
+ catch (e) {
618
+ // GO-FIX 1 (achado 7): o erro de sistema (mkdir, open, spawn) também volta tipado.
619
+ const motivo = e.message;
620
+ return falha(motivo.startsWith('runtime.unavailable:') ? motivo : `runtime.unavailable: ${motivo}`, { comando });
621
+ }
622
+ try {
623
+ gravarConfigDaWorktree(pedido.cwd, configDoFilho({ url: servidor.url, bearerTokenEnvVar: grok_mcp_1.ENV_DO_TOKEN_DO_SERVIDOR_GROK }, contexto.threadId));
624
+ }
625
+ catch (e) {
626
+ servidor.encerrar();
627
+ return falha(`runtime.unavailable: ${e.message}`, { comando });
628
+ }
629
+ }
630
+ const ambienteDoGrok = servidor ? { ...ambiente, [grok_mcp_1.ENV_DO_TOKEN_DO_SERVIDOR_GROK]: servidor.token } : ambiente;
631
+ let sensores;
632
+ try {
633
+ sensores = (0, codex_runner_1.iniciarSupervisor)(comando, pedido.cwd, logPath, ambienteDoGrok);
634
+ }
635
+ catch (e) {
636
+ servidor?.encerrar();
637
+ return falha(`runtime.unavailable: o supervisor do grok nao iniciou (${e.code ?? 'erro'})`, { comando });
638
+ }
639
+ const base = { comando, logPath, ...sensores };
640
+ const casa = casaDoGrok(ambiente);
641
+ const prazo = Date.now() + (pedido.esperaMs ?? exports.ESPERA_PADRAO_MS);
642
+ for (;;) {
643
+ if (acharSessaoGrok(sessionId, casa))
644
+ return { ok: true, sessionId, verificada: true, stdout: '', stderr: '', ...base };
645
+ const recibo = lerMetadado(sensores.reciboPath);
646
+ if (recibo) {
647
+ servidor?.encerrar();
648
+ return falhaAntesDaSessao(recibo, sessionId, base);
649
+ }
650
+ if (Date.now() >= prazo)
651
+ break;
652
+ Atomics.wait(pausa, 0, 0, 100);
653
+ }
654
+ const recibo = lerMetadado(sensores.reciboPath);
655
+ if (recibo) {
656
+ servidor?.encerrar();
657
+ return falhaAntesDaSessao(recibo, sessionId, base);
658
+ }
659
+ // Filho vivo sem a pasta no prazo: a sessão segue, sem prova de existência ainda; o watcher a acompanha.
660
+ const processo = lerMetadado(sensores.processoPath);
661
+ if (processo?.filho && (0, codex_runner_1.estadoProcesso)(processo.filho) === 'vivo') {
662
+ return { ok: true, sessionId, verificada: false, stdout: '', stderr: '', ...base };
663
+ }
664
+ servidor?.encerrar();
665
+ return falha('runtime.unavailable: o supervisor do grok nao registrou o processo vivo no prazo', base);
666
+ }
667
+ /**
668
+ * O processo terminou antes de a sessão existir. A falha da conta vem do objeto `error` do stream e, sem ele, do stderr
669
+ * do próprio runtime; o rate limit de janela curta volta tipado para a fila; o resto é `runtime.unavailable` com o código.
670
+ */
671
+ function falhaAntesDaSessao(recibo, sessionId, base) {
672
+ const estado = (0, grok_events_1.lerStreamGrok)(base.logPath, sessionId);
673
+ const cauda = caudaDoStderr(`${base.logPath}.stderr`);
674
+ const mensagem = estado?.erro?.mensagem ?? '';
675
+ const texto = [mensagem, cauda].filter(Boolean).join('\n');
676
+ const falhaDeConta = (0, grok_events_1.falhaDoErroGrok)(estado?.erro ?? null) ?? (cauda ? (0, grok_events_1.falhaDeContaDoTextoGrok)(cauda) : null);
677
+ const rateLimit = falhaDeConta ? null : (0, claude_bg_1.parseRateLimit)(texto);
678
+ // Ao ledger vai o fim da cauda, onde o erro mora; quem grava corta pelo começo.
679
+ const stderr = fimDoTexto(cauda, LIMITE_STDERR_NO_RESULTADO);
680
+ // O erro de spawn (o binário sumiu entre a conferência e o supervisor) vem antes do código, que o Node dá negativo.
681
+ const codigo = recibo.erro ?? recibo.exitCode ?? recibo.signal ?? 'sem codigo';
682
+ const erro = falhaDeConta ? `${falhaDeConta.motivo}: ${falhaDeConta.trecho}`
683
+ : rateLimit ? `limite de uso do runtime atingido: ${rateLimit.trecho}`
684
+ : `runtime.unavailable: o grok saiu ${codigo} antes de criar a sessao${mensagem ? `: ${mensagem}` : ''}`;
685
+ return { ok: false, sessionId: null, verificada: false, stdout: '', stderr, erro, falhaDeConta, rateLimit, ...base };
686
+ }