@orkastery/cli 0.6.3 → 0.6.5

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.
@@ -62,6 +62,7 @@ const session_watcher_grok_1 = require("./session-watcher-grok");
62
62
  const conta_esgotada_1 = require("./conta-esgotada");
63
63
  const trabalho_thread_1 = require("./trabalho-thread");
64
64
  const pedido_conducao_1 = require("./pedido-conducao");
65
+ const codex_task_complete_1 = require("./codex-task-complete");
65
66
  /** O gate estreito da própria sessão vence diagnósticos genéricos do watcher. */
66
67
  function pedidoDaConducao(eventos, sessionId, despachoEm) {
67
68
  const evento = eventos.filter(e => e.tipo === 'gate_blocked' && e.origem === 'mcp.request_conduction' &&
@@ -389,6 +390,16 @@ function observarSessao(carregado, sessionId, opcoes = {}) {
389
390
  return observarSessaoResolvida(carregado, sessionId, resolvida, opcoes);
390
391
  }
391
392
  catch (e) {
393
+ if (e.code === 'SESSION_STATE_SPLIT' && threadId) {
394
+ const thread = (0, thread_1.lerThread)(carregado.raiz, threadId);
395
+ const registrada = thread.sessoes.find(s => s.sessionId === sessionId);
396
+ if (registrada) {
397
+ const sessao = (0, session_events_1.exigirSessaoDespachada)(registrada, sessionId);
398
+ const prova = (0, codex_task_complete_1.taskCompleteFixadoDaSessao)(carregado.raiz, thread, sessao, (0, ledger_1.lerLedger)((0, thread_1.dirThread)(carregado.raiz, threadId)), opcoes.agoraMs ?? Date.now());
399
+ if (prova)
400
+ return observarSessaoResolvida(carregado, sessionId, { thread, sessao }, { ...opcoes, taskCompleteFixado: prova });
401
+ }
402
+ }
392
403
  if (threadId)
393
404
  tentarRegistrarErroWatcher(carregado.raiz, threadId, sessionId, e);
394
405
  throw e;
@@ -542,7 +553,8 @@ function observarSessaoResolvida(carregado, sessionId, { thread, sessao: registr
542
553
  return desfazer();
543
554
  if (sessao.runtime === 'claude-bg') {
544
555
  const r = comLockWatcher(pasta, () => (0, session_watcher_claude_1.observarClaudeSobLock)({ raiz: carregado.raiz,
545
- thread, sessao, dir, pasta, chave, agoraMs: now, limiteMs: exports.LIMITE_MORTE_MS, consulta: opcoes.consultaClaude, carregado })) ?? { ...base, ocupado: true };
556
+ thread, sessao, dir, pasta, chave, agoraMs: now, limiteMs: exports.LIMITE_MORTE_MS, consulta: opcoes.consultaClaude,
557
+ tela: opcoes.telaClaude, carregado })) ?? { ...base, ocupado: true };
546
558
  return (0, ledger_1.threadPresente)(dir) ? r : desfazer();
547
559
  }
548
560
  // RM-067 (F2): o grok tem observador próprio (stream, recibo do supervisor e pasta da sessão), no mesmo lock.
@@ -565,6 +577,7 @@ function observarSessaoResolvida(carregado, sessionId, { thread, sessao: registr
565
577
  const perfil = (0, runtime_profiles_1.perfilDoRegistro)(eventos, sessao);
566
578
  // Fonte do controller validada ANTES de ler rollout ou escrever cursor.
567
579
  let fonteControlador = null, snapshot = null;
580
+ let taskCompleteFixado = opcoes.taskCompleteFixado ?? null;
568
581
  let cwdDespacho;
569
582
  if (registro?.controlador) {
570
583
  const despacho = eventos.filter(e => e.tipo === 'phase_dispatch' && e.sessionId === sessionId).at(-1);
@@ -580,7 +593,16 @@ function observarSessaoResolvida(carregado, sessionId, { thread, sessao: registr
580
593
  vinculo: { thread: thread.id, fase: sessao.fase, promptSha256: sessao.promptSha256 }
581
594
  });
582
595
  // Relógio injetado vale como está; o real é lido pelo sensor depois do state.json.
583
- snapshot = (0, codex_controller_sensor_1.lerSnapshotController)(fonteControlador, opcoes.agoraMs === undefined ? {} : { agoraMs: now });
596
+ try {
597
+ snapshot = (0, codex_controller_sensor_1.lerSnapshotController)(fonteControlador, opcoes.agoraMs === undefined ? {} : { agoraMs: now });
598
+ }
599
+ catch (e) {
600
+ // A varredura integral e excepcional: no fluxo saudável o cursor incremental
601
+ // reconhece task_complete sem reler todo o rollout a cada polling.
602
+ taskCompleteFixado ??= (0, codex_task_complete_1.taskCompleteFixadoDaFonte)(fonteControlador, now);
603
+ if (!taskCompleteFixado)
604
+ throw e;
605
+ }
584
606
  }
585
607
  const file = path.join(pasta, 'cursor.json');
586
608
  const cursor = lerJson(file) ?? { schema: 'ork.session-cursor/v1', sessionId,
@@ -590,7 +612,7 @@ function observarSessaoResolvida(carregado, sessionId, { thread, sessao: registr
590
612
  // Cursor anterior media bytes, nao progresso: migracao conservadora desde o despacho.
591
613
  cursor.progressoEm ??= Date.parse(sessao.despachadaEm);
592
614
  // Transporte do controller não admite descoberta nem override de rollout.
593
- const fontes = { stream: registro?.logPath, rollout: snapshot ? snapshot.rollout
615
+ const fontes = { stream: registro?.logPath, rollout: fonteControlador ? fonteControlador.rollout
594
616
  : opcoes.rollout === undefined ? (0, codex_1.acharRollout)(sessionId, 3, (0, codex_1.casaDoCodex)(perfil)) : opcoes.rollout };
595
617
  let bytesLidos = 0, temMais = false, cresceu = false;
596
618
  for (const [nome, fonte] of Object.entries(fontes)) {
@@ -673,7 +695,10 @@ function observarSessaoResolvida(carregado, sessionId, { thread, sessao: registr
673
695
  return tempo || (y.numeroTurno ?? 0) - (x.numeroTurno ?? 0) ||
674
696
  ordem[x.classificacao] - ordem[y.classificacao] || a.nome.localeCompare(b.nome);
675
697
  });
676
- let terminal = terminais[0]?.terminal;
698
+ const taskCompleteDoCursor = fonteControlador ? terminais.find(({ nome, terminal: atual }) => nome === 'rollout' && atual.fonte === 'rollout.task_complete')?.terminal : undefined;
699
+ let terminal = taskCompleteFixado?.terminal ?? taskCompleteDoCursor ?? terminais[0]?.terminal;
700
+ const taskCompleteAutenticado = terminal !== undefined &&
701
+ (terminal === taskCompleteFixado?.terminal || terminal === taskCompleteDoCursor);
677
702
  const fontesAtuais = Object.values(cursor.fontes);
678
703
  const aberto = fontesAtuais.some(f => {
679
704
  if (!f.parser.turnoAberto)
@@ -700,22 +725,23 @@ function observarSessaoResolvida(carregado, sessionId, { thread, sessao: registr
700
725
  const invalido = fontesAtuais.some(f => f.parser.invalido);
701
726
  // Conclusão nativa é medida em todo terminal, independente da política de retry:
702
727
  // bloqueio sem retry preserva o motivo, mas não prova que o turno do runtime terminou.
703
- const provaNativa = snapshot && terminal
728
+ const provaNativa = taskCompleteAutenticado ? null : snapshot && terminal
704
729
  ? semProvaNativa(snapshot, terminal, terminais[0]?.nome, fechamento, sessionId, cwdDespacho)
705
730
  : 'terminal nativo do controller ausente';
706
731
  // Parecer negativo também exige conclusão nativa inteira: texto claims.failed de
707
732
  // turno interrompido não pode virar rejeição autenticada de um objetivo.
708
- const semProva = snapshot && terminal && terminal.classificacao !== 'human.pending' && !bloqueioPreservado(terminal)
733
+ const semProva = !taskCompleteAutenticado && snapshot && terminal && terminal.classificacao !== 'human.pending' && !bloqueioPreservado(terminal)
709
734
  ? provaNativa : null;
710
735
  const descorrelacionado = !!semProva;
711
- const incompleto = semCloseNoLimite && (!!terminal || estado !== 'ausente' || aberto || parcial || invalido) || !backlog && (((parcial || aberto || invalido || descorrelacionado || !terminal) && !!fechamento) ||
736
+ const observacaoInvalida = !taskCompleteAutenticado && (invalido || aberto || descorrelacionado);
737
+ const incompleto = !taskCompleteAutenticado && (semCloseNoLimite && (!!terminal || estado !== 'ausente' || aberto || parcial || invalido) || !backlog && (((parcial || aberto || invalido || descorrelacionado || !terminal) && !!fechamento) ||
712
738
  (estavel && (parcial || (aberto && estado === 'ausente') ||
713
- (supervisionado && estado === 'desconhecido'))));
739
+ (supervisionado && estado === 'desconhecido')))));
714
740
  const morte = estado === 'ausente' && estavel && !temMais;
715
741
  // Fechamento supervisionado precisa existir antes de associar o código de saída ao turno.
716
- const podeTerminar = !esperaHumana && (incompleto || (!temMais && !aberto &&
742
+ const podeTerminar = taskCompleteAutenticado || !esperaHumana && (incompleto || (!temMais && !aberto &&
717
743
  ((terminal && (fechamento || !supervisionado)) || morte)));
718
- const conta = falhaDeContaDoTerminal(terminal, !invalido && !aberto, snapshot, now);
744
+ const conta = falhaDeContaDoTerminal(terminal, taskCompleteAutenticado || !invalido && !aberto, snapshot, now);
719
745
  (0, codex_runner_1.gravarAtomico)(file, cursor);
720
746
  return (0, session_events_1.comLockDeSessao)(dir, () => {
721
747
  const atuais = (0, ledger_1.lerLedger)(dir);
@@ -735,9 +761,9 @@ function observarSessaoResolvida(carregado, sessionId, { thread, sessao: registr
735
761
  const pedido = pedidoDaConducao(atuais, sessionId, sessao.despachadaEm);
736
762
  const semTerminal = !terminal;
737
763
  // Fato registrado para a autoridade de revisão: `null` só com terminal nativo correlacionado.
738
- const conclusaoNativa = incompleto || invalido || aberto || descorrelacionado
764
+ const conclusaoNativa = incompleto || observacaoInvalida
739
765
  ? provaNativa ?? 'observação incompleta ou inválida' : provaNativa;
740
- if (incompleto || invalido || aberto || descorrelacionado)
766
+ if (incompleto || observacaoInvalida)
741
767
  terminal = { classificacao: 'gate_blocked', motivo: 'runtime.unavailable',
742
768
  timestamp: new Date(now).toISOString(), turnId: null, fonte: semProva ?? 'observação incompleta ou inválida',
743
769
  tokens: new codex_events_1.ParserCodex().estado.tokens, duracaoMs: null };
@@ -745,12 +771,12 @@ function observarSessaoResolvida(carregado, sessionId, { thread, sessao: registr
745
771
  terminal = { classificacao: 'gate_blocked', motivo: 'runtime.silencio',
746
772
  timestamp: new Date(now).toISOString(), turnId: null, fonte: 'processo ausente e arquivos estáveis',
747
773
  tokens: new codex_events_1.ParserCodex().estado.tokens, duracaoMs: null };
748
- if (pedido && (terminal.classificacao === 'fase_concluida' ||
774
+ if (pedido && (taskCompleteAutenticado || terminal.classificacao === 'fase_concluida' ||
749
775
  terminal.classificacao === 'gate_blocked' && ['runtime.unavailable', 'runtime.silencio'].includes(terminal.motivo ?? ''))) {
750
776
  terminal = { ...terminal, classificacao: 'gate_blocked', motivo: pedido.motivo,
751
777
  fonte: `mcp.request_conduction: ${pedido.detalhe}` };
752
778
  }
753
- if (fechamento && (fechamento.exitCode !== 0 || fechamento.signal || fechamento.erro) &&
779
+ if (!taskCompleteAutenticado && fechamento && (fechamento.exitCode !== 0 || fechamento.signal || fechamento.erro) &&
754
780
  terminal.classificacao !== 'human.pending' && !bloqueioPreservado(terminal)) {
755
781
  terminal = { ...terminal, classificacao: 'gate_blocked', motivo: 'runtime.unavailable' };
756
782
  }
@@ -761,16 +787,19 @@ function observarSessaoResolvida(carregado, sessionId, { thread, sessao: registr
761
787
  fonte: `${conta.fonte}: ${conta.falha.motivo} (${conta.falha.trecho})` };
762
788
  const respostaFinal = terminal.respostaFinal
763
789
  ? (0, trabalho_thread_1.gravarRespostaFinalDaSessao)(carregado.raiz, thread.id, sessionId, terminal.respostaFinal) : null;
790
+ const taskCompleteBloqueado = taskCompleteAutenticado && terminal.classificacao !== 'fase_concluida';
764
791
  const dados = { fase: sessao.fase, sessionId, despachoEm: sessao.despachadaEm, sensorResultId: resultadoId,
765
792
  classificacao: terminal.classificacao, motivo: terminal.motivo, runtime: 'codex', fonte: terminal.fonte,
766
- fonteVencedora: pedido || incompleto || invalido || aberto || descorrelacionado ? null : terminais[0]?.nome ?? null,
793
+ fonteVencedora: pedido ? null : taskCompleteAutenticado ? 'rollout'
794
+ : incompleto || invalido || aberto || descorrelacionado ? null : terminais[0]?.nome ?? null,
767
795
  turnId: terminal.turnId, numeroTurno: terminal.numeroTurno ?? null,
768
796
  tokens: terminal.tokens, exitCode: fechamento?.exitCode ?? null, exitCodeFonte: fechamento?.fonte ?? 'unavailable',
769
797
  signal: fechamento?.signal ?? null, duracaoMs: fechamento?.duracaoMs ?? terminal.duracaoMs,
770
798
  duracaoFonte: fechamento?.fonte ?? (terminal.duracaoMs === null ? 'unavailable' : terminal.fonte),
771
- ok: terminal.classificacao === 'fase_concluida' && fechamento?.exitCode === 0,
772
- conclusaoNativa: conclusaoNativa === null,
773
- conclusaoNativaAusente: conclusaoNativa,
799
+ ok: terminal.classificacao === 'fase_concluida' && (taskCompleteAutenticado || fechamento?.exitCode === 0),
800
+ conclusaoNativa: conclusaoNativa === null && !taskCompleteBloqueado,
801
+ conclusaoNativaAusente: taskCompleteBloqueado
802
+ ? 'task_complete encerrou o turno com resultado bloqueado' : conclusaoNativa,
774
803
  estado: terminal.classificacao === 'fase_concluida' ? 'concluida' : 'bloqueada',
775
804
  observadoEm: terminal.timestamp, gate: 'phase.dispatch', origem: 'sessions.watch',
776
805
  morteLatenciaMs: morte ? now - cursor.progressoEm : null, ...(perfil ? { perfil } : {}),
package/dist/ship.js CHANGED
@@ -51,6 +51,7 @@ var __importStar = (this && this.__importStar) || (function () {
51
51
  })();
52
52
  Object.defineProperty(exports, "__esModule", { value: true });
53
53
  exports.MODOS_QUE_AUTORIZAM_PUSH = void 0;
54
+ exports.verificacaoCompletaParaShip = verificacaoCompletaParaShip;
54
55
  exports.commitQueIncorporou = commitQueIncorporou;
55
56
  exports.autorizacaoDePush = autorizacaoDePush;
56
57
  exports.arvoreComBranch = arvoreComBranch;
@@ -88,6 +89,10 @@ const mcp_ship_transporte_1 = require("./mcp-ship-transporte");
88
89
  const branch_de_estado_1 = require("./branch-de-estado");
89
90
  const locale_1 = require("./locale");
90
91
  const noctum_guarda_1 = require("./noctum-guarda");
92
+ /** RM-081 F3: `ok` nunca basta quando a rodada nao cobriu a suite inteira. */
93
+ function verificacaoCompletaParaShip(verificacao) {
94
+ return verificacao.ok && !verificacao.parcial;
95
+ }
91
96
  /**
92
97
  * RM-037 (fatia 5, A1): o commit de primeiro pai da base que incorporou o `shaDe`: o merge que trouxe a branch, ou
93
98
  * o proprio `shaDe` quando a base avancou por fast-forward ate ele. Com a branch ja incorporada, a ponta da base e
@@ -556,9 +561,10 @@ function ship(carregado, threadId, opcoes) {
556
561
  const verificacao = (0, verify_1.verificar)(carregado, threadId, { executor: opcoes.executorVerify,
557
562
  conducao: { filaPesada: { ...opcoes.avisoDaFilaPesada, esperarMs: opcoes.esperarFilaPesadaMs } } });
558
563
  r.verificacao = verificacao;
559
- if (!verificacao.ok) {
560
- const motivo = verificacao.motivos.find((m) => m !== 'claims.unverifiable');
561
- return bloquear(motivo, (0, locale_1.textoDoCatalogo)((m) => m.ship.verificacaoReprovou(verificacao.commit, verificacao.motivos.join(', '))), (0, locale_1.textoDoCatalogo)((m) => m.ship.rodeVerify(threadId)));
564
+ if (!verificacaoCompletaParaShip(verificacao)) {
565
+ const motivo = (verificacao.parcial ? 'verify.failed'
566
+ : verificacao.motivos.find((m) => m !== 'claims.unverifiable'));
567
+ return bloquear(motivo, (0, locale_1.textoDoCatalogo)((m) => m.ship.verificacaoReprovou(verificacao.commit, verificacao.parcial ? 'verify.parcial' : verificacao.motivos.join(', '))), (0, locale_1.textoDoCatalogo)((m) => m.ship.rodeVerify(threadId)));
562
568
  }
563
569
  // Referencias locais, resolvidas antes de tomar o lease.
564
570
  const shaDe = shaDaRef(raiz, `refs/heads/${de}`);