@leg3ndy/otto-bridge 1.1.5 → 1.1.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/README.md CHANGED
@@ -15,7 +15,7 @@ Para o estado atual da arquitetura, capacidades entregues, limitacoes e roadmap
15
15
 
16
16
  Para o corte de arquitetura do `0.9.0`, veja [`leg3ndy-ai-backend/docs/otto-bridge/releases/OTTO_BRIDGE_0_9_0_RELEASE.md`](../leg3ndy-ai-backend/docs/otto-bridge/releases/OTTO_BRIDGE_0_9_0_RELEASE.md).
17
17
 
18
- Para o patch atual `1.1.5`, veja [`leg3ndy-ai-backend/docs/otto-bridge/releases/OTTO_BRIDGE_1_1_5_PATCH.md`](../leg3ndy-ai-backend/docs/otto-bridge/releases/OTTO_BRIDGE_1_1_5_PATCH.md). Para o corte funcional da linha `1.1.0`, veja [`leg3ndy-ai-backend/docs/otto-bridge/releases/OTTO_BRIDGE_1_1_0_RELEASE.md`](../leg3ndy-ai-backend/docs/otto-bridge/releases/OTTO_BRIDGE_1_1_0_RELEASE.md).
18
+ Para o patch atual `1.1.6`, veja [`leg3ndy-ai-backend/docs/otto-bridge/releases/OTTO_BRIDGE_1_1_6_PATCH.md`](../leg3ndy-ai-backend/docs/otto-bridge/releases/OTTO_BRIDGE_1_1_6_PATCH.md). Para o corte funcional da linha `1.1.0`, veja [`leg3ndy-ai-backend/docs/otto-bridge/releases/OTTO_BRIDGE_1_1_0_RELEASE.md`](../leg3ndy-ai-backend/docs/otto-bridge/releases/OTTO_BRIDGE_1_1_0_RELEASE.md).
19
19
 
20
20
  ## Distribuicao
21
21
 
@@ -38,14 +38,14 @@ Enquanto o pacote nao estiver publicado, voce pode gerar um tarball local:
38
38
 
39
39
  ```bash
40
40
  npm pack
41
- npm install -g ./leg3ndy-otto-bridge-1.1.5.tgz
41
+ npm install -g ./leg3ndy-otto-bridge-1.1.6.tgz
42
42
  ```
43
43
 
44
- Na linha `1.1.5`, `playwright` segue como dependencia obrigatoria no `otto-bridge`. O primeiro `npm install -g @leg3ndy/otto-bridge` pode demorar mais porque instala o browser persistente usado pelo WhatsApp Web e pelos fluxos web em background do bridge.
44
+ Na linha `1.1.6`, `playwright` segue como dependencia obrigatoria no `otto-bridge`. O primeiro `npm install -g @leg3ndy/otto-bridge` pode demorar mais porque instala o browser persistente usado pelo WhatsApp Web e pelos fluxos web em background do bridge.
45
45
 
46
- No macOS, a linha `1.1.5` usa o provider `macos-helper`, um helper `WKWebView` sem Dock para o WhatsApp Web. O helper sobe com user-agent de Chrome moderno para evitar o bloqueio do WhatsApp ao detectar Safari/WebKit. O runtime antigo com Chromium/Playwright fica disponivel apenas como override explicito via `OTTO_BRIDGE_WHATSAPP_RUNTIME_PROVIDER=embedded-playwright`.
46
+ No macOS, a linha `1.1.6` usa o provider `macos-helper`, um helper `WKWebView` sem Dock para o WhatsApp Web. O helper sobe com user-agent de Chrome moderno para evitar o bloqueio do WhatsApp ao detectar Safari/WebKit. O runtime antigo com Chromium/Playwright fica disponivel apenas como override explicito via `OTTO_BRIDGE_WHATSAPP_RUNTIME_PROVIDER=embedded-playwright`.
47
47
 
48
- No nivel arquitetural, o `0.9.0` marcou a mudanca de papel do bridge: ele publica tools e resultados estruturados para o Otto, em vez de injetar resposta pronta como caminho principal do chat. O `1.0.0` oficializou isso como runtime agentico; o `1.1.5` mantem a camada workspace-first com rail de coding, trust/policy por workspace, source control first-class, working set persistido e grounding remoto por repositório, enquanto corrige o `Otto Console` para voltar ao scrollback real, aceitar digitação enquanto o Otto streama e reaproveitar a tela do hub sem duplicar o banner.
48
+ No nivel arquitetural, o `0.9.0` marcou a mudanca de papel do bridge: ele publica tools e resultados estruturados para o Otto, em vez de injetar resposta pronta como caminho principal do chat. O `1.0.0` oficializou isso como runtime agentico; o `1.1.6` mantem a camada workspace-first com rail de coding, trust/policy por workspace, source control first-class, working set persistido e grounding remoto por repositório, enquanto endurece o `Otto Console` para manter o scrollback real sem vazar separadores do rodape e deduplicar chunks de stream sobrepostos.
49
49
 
50
50
  ## Publicacao
51
51
 
@@ -170,7 +170,7 @@ Esse comando abre um shell local interativo para instalar extensoes, rodar coman
170
170
 
171
171
  ### WhatsApp Web em background
172
172
 
173
- Fluxo recomendado na linha `1.1.5`:
173
+ Fluxo recomendado na linha `1.1.6`:
174
174
 
175
175
  ```bash
176
176
  otto-bridge extensions --install whatsappweb
@@ -180,13 +180,13 @@ otto-bridge extensions --status whatsappweb
180
180
 
181
181
  O setup agora abre o login do WhatsApp Web no helper/background browser do proprio bridge. Depois do QR code, o Otto usa a sessao local em background, sem depender de aba visivel no Safari.
182
182
 
183
- Contrato da linha `1.1.5`:
183
+ Contrato da linha `1.1.6`:
184
184
 
185
185
  - `otto-bridge extensions --setup whatsappweb`: autentica a sessao uma vez
186
186
  - `otto-bridge`: mantem o browser persistente do WhatsApp vivo em background enquanto o runtime do hub estiver ativo, sem depender de uma aba aberta no Safari
187
187
  - ao fechar o `otto-bridge`: o browser em background e desligado, mas a sessao local fica lembrada para o proximo boot
188
188
 
189
- ## Handoff rapido da linha 1.1.5
189
+ ## Handoff rapido da linha 1.1.6
190
190
 
191
191
  Ja fechado no codigo:
192
192
 
@@ -552,7 +552,8 @@ class ConsoleScreenRenderer {
552
552
  slashSuggestions = [];
553
553
  selectedSuggestionIndex = 0;
554
554
  renderedOnce = false;
555
- renderedCursorLineIndex = 0;
555
+ renderedLineCount = 0;
556
+ renderedTopRow = 0;
556
557
  constructor(modelMode, approvalMode, headerFactory = () => []) {
557
558
  this.modelMode = modelMode;
558
559
  this.approvalMode = approvalMode;
@@ -581,6 +582,8 @@ class ConsoleScreenRenderer {
581
582
  clearTranscript() {
582
583
  this.liveEntries.splice(0, this.liveEntries.length);
583
584
  this.renderedOnce = false;
585
+ this.renderedLineCount = 0;
586
+ this.renderedTopRow = 0;
584
587
  clearScreen();
585
588
  output.write(this.headerFactory().join("\n"));
586
589
  output.write("\n\n");
@@ -682,19 +685,20 @@ class ConsoleScreenRenderer {
682
685
  getRenderWidth() {
683
686
  return Math.max(48, Number(output.columns || 96));
684
687
  }
688
+ getRenderHeight() {
689
+ return Math.max(10, Number(output.rows || 24));
690
+ }
685
691
  clearRenderBlock() {
686
692
  if (!this.renderedOnce) {
687
693
  return;
688
694
  }
689
695
  output.write("\u001b[?25l");
690
- cursorTo(output, 0);
691
- if (this.renderedCursorLineIndex > 0) {
692
- moveCursor(output, 0, -this.renderedCursorLineIndex);
693
- }
696
+ cursorTo(output, 0, this.renderedTopRow);
694
697
  clearScreenDown(output);
695
698
  output.write("\u001b[?25h");
696
699
  this.renderedOnce = false;
697
- this.renderedCursorLineIndex = 0;
700
+ this.renderedLineCount = 0;
701
+ this.renderedTopRow = 0;
698
702
  }
699
703
  printLinesAbove(lines) {
700
704
  if (!this.active) {
@@ -716,13 +720,13 @@ class ConsoleScreenRenderer {
716
720
  }
717
721
  const enabled = supportsAnsi();
718
722
  const width = this.getRenderWidth();
723
+ const height = this.getRenderHeight();
719
724
  const separator = style("─".repeat(width), ANSI.brandBlue, enabled);
720
725
  const composer = renderConsoleComposerLines(this.draftValue, width, enabled);
721
726
  const suggestionLines = buildConsoleSlashSuggestionLines(this.slashSuggestions, this.selectedSuggestionIndex, width, enabled);
722
727
  const usageTokens = Math.min(estimateConsoleContextTokens(this.conversationMessages, this.draftValue), getCliModelContextWindowTokens(this.modelMode));
723
728
  const liveLines = this.liveEntries.flatMap((entry) => this.buildEntryLines(entry, width));
724
- const blockLines = [
725
- ...liveLines,
729
+ const footerLines = [
726
730
  separator,
727
731
  ...composer.renderedLines,
728
732
  ...suggestionLines,
@@ -730,17 +734,24 @@ class ConsoleScreenRenderer {
730
734
  buildConsoleFooterStatusLine(width, this.modelMode, usageTokens, enabled),
731
735
  buildConsoleFooterApprovalLine(this.approvalMode, enabled, this.approvalStatusSuffix),
732
736
  ];
737
+ const maxVisibleLiveLines = Math.max(0, height - footerLines.length);
738
+ const visibleLiveLines = maxVisibleLiveLines > 0
739
+ ? liveLines.slice(-maxVisibleLiveLines)
740
+ : [];
741
+ const blockLines = [
742
+ ...visibleLiveLines,
743
+ ...footerLines,
744
+ ];
745
+ const topRow = Math.max(0, height - blockLines.length);
733
746
  this.clearRenderBlock();
734
747
  output.write("\u001b[?25l");
748
+ cursorTo(output, 0, topRow);
735
749
  output.write(blockLines.join("\n"));
736
750
  this.renderedOnce = true;
737
- this.renderedCursorLineIndex = liveLines.length + 1 + composer.cursorLineIndex;
738
- const linesBelowCursor = blockLines.length - 1 - this.renderedCursorLineIndex;
739
- cursorTo(output, Math.min(width - 1, CONSOLE_COMPOSER_CURSOR_COLUMN + composer.cursorColumn));
740
- if (linesBelowCursor > 0) {
741
- moveCursor(output, 0, -linesBelowCursor);
742
- }
743
- cursorTo(output, Math.min(width - 1, CONSOLE_COMPOSER_CURSOR_COLUMN + composer.cursorColumn));
751
+ this.renderedLineCount = blockLines.length;
752
+ this.renderedTopRow = topRow;
753
+ const composerRow = topRow + visibleLiveLines.length + 1 + composer.cursorLineIndex;
754
+ cursorTo(output, Math.min(width - 1, CONSOLE_COMPOSER_CURSOR_COLUMN + composer.cursorColumn), composerRow);
744
755
  output.write("\u001b[?25h");
745
756
  }
746
757
  }
@@ -1455,6 +1466,29 @@ export function tryConsumeControlSequence(buffer) {
1455
1466
  }
1456
1467
  return null;
1457
1468
  }
1469
+ export function resolveConsoleStreamDelta(previousText, incomingChunk) {
1470
+ const previous = previousText || "";
1471
+ const incoming = incomingChunk || "";
1472
+ if (!incoming) {
1473
+ return "";
1474
+ }
1475
+ if (!previous) {
1476
+ return incoming;
1477
+ }
1478
+ if (incoming === previous || previous.endsWith(incoming)) {
1479
+ return "";
1480
+ }
1481
+ if (incoming.startsWith(previous)) {
1482
+ return incoming.slice(previous.length);
1483
+ }
1484
+ const maxOverlap = Math.min(previous.length, incoming.length);
1485
+ for (let overlap = maxOverlap; overlap > 0; overlap -= 1) {
1486
+ if (previous.slice(-overlap) === incoming.slice(0, overlap)) {
1487
+ return incoming.slice(overlap);
1488
+ }
1489
+ }
1490
+ return incoming;
1491
+ }
1458
1492
  class ConsoleInputController {
1459
1493
  rl;
1460
1494
  ui;
@@ -2484,6 +2518,7 @@ async function runOttoConsole(rl, config, runtimeSession, options) {
2484
2518
  conversation.push({ role: "user", content: normalizedPrompt });
2485
2519
  renderConversationState();
2486
2520
  let streamedAssistant = "";
2521
+ let streamedReasoning = "";
2487
2522
  let assistantEntryId = null;
2488
2523
  let assistantPrefixPrinted = false;
2489
2524
  let reasoningPrefixPrinted = false;
@@ -2513,7 +2548,7 @@ async function runOttoConsole(rl, config, runtimeSession, options) {
2513
2548
  throw new Error(errorMessage);
2514
2549
  }
2515
2550
  const reasoningChunk = typeof event.content === "string" && eventType === "reasoning"
2516
- ? event.content
2551
+ ? resolveConsoleStreamDelta(streamedReasoning, event.content)
2517
2552
  : "";
2518
2553
  if (reasoningChunk) {
2519
2554
  if (!reasoningPrefixPrinted) {
@@ -2529,9 +2564,12 @@ async function runOttoConsole(rl, config, runtimeSession, options) {
2529
2564
  else {
2530
2565
  output.write(style(reasoningChunk, ANSI.slateItalic, supportsAnsi()));
2531
2566
  }
2567
+ streamedReasoning += reasoningChunk;
2532
2568
  return;
2533
2569
  }
2534
- const contentChunk = typeof event.content === "string" ? event.content : "";
2570
+ const contentChunk = typeof event.content === "string"
2571
+ ? resolveConsoleStreamDelta(streamedAssistant, event.content)
2572
+ : "";
2535
2573
  if (!contentChunk) {
2536
2574
  return;
2537
2575
  }
package/dist/types.js CHANGED
@@ -1,5 +1,5 @@
1
1
  export const BRIDGE_CONFIG_VERSION = 1;
2
- export const BRIDGE_VERSION = "1.1.5";
2
+ export const BRIDGE_VERSION = "1.1.6";
3
3
  export const BRIDGE_PACKAGE_NAME = "@leg3ndy/otto-bridge";
4
4
  export const DEFAULT_API_BASE_URL = "http://localhost:8000";
5
5
  export const DEFAULT_POLL_INTERVAL_MS = 3000;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@leg3ndy/otto-bridge",
3
- "version": "1.1.5",
3
+ "version": "1.1.6",
4
4
  "private": false,
5
5
  "type": "module",
6
6
  "description": "Local companion for Otto Bridge device pairing and WebSocket runtime.",
@@ -24,7 +24,7 @@ if (!existsSync(mainPath)) {
24
24
  process.exit(0);
25
25
  }
26
26
 
27
- console.log("\n[otto-bridge] Welcome to OTTOAI 1.1.5");
27
+ console.log("\n[otto-bridge] Welcome to OTTOAI 1.1.6");
28
28
  console.log("[otto-bridge] Vamos iniciar o setup interativo do bridge.\n");
29
29
 
30
30
  const result = spawnSync(process.execPath, [mainPath, "setup", "--postinstall"], {