Skip to content

Surface streamed chat errors in the chat UI#7

Open
jakimoai wants to merge 1 commit into
jjang-ai:mainfrom
jakimoai:jack/chat-sse-error-surfacing
Open

Surface streamed chat errors in the chat UI#7
jakimoai wants to merge 1 commit into
jjang-ai:mainfrom
jakimoai:jack/chat-sse-error-surfacing

Conversation

@jakimoai

@jakimoai jakimoai commented Jul 11, 2026

Copy link
Copy Markdown

Summary

Surfaces top-level streamed chat errors in the current assistant message instead of leaving a blank response.

Problem

When the engine emits an SSE chunk containing a top-level error object, the chat UI leaves the assistant response blank. The backend failure is present on the wire, but the user sees no output.

Root Cause

The Swift streaming loop handles warning and usage chunks, then expects choices[].delta. Top-level stream errors do not include choices, so they were discarded before reaching visible chat state.

Fix

  • Parse object-form error.message and error.detail payloads.
  • Parse string-form error payloads.
  • Populate the current assistant message and end the failed stream with an error finish reason.
  • Preserve the existing content, reasoning, usage, tool-call, and finish-reason paths.

No engine, model launch, cache, or runtime behavior changes.

Visual Proof

The same local mock SSE request was captured before and after the change:

  • Before: the completed turn contains a user message and no visible assistant text.
  • After: the assistant message displays the exact streamed backend error.
  • Regression: a normal choices[].delta.content stream still displays its response.

Proven Live on the Dev App

Case Result
Top-level object-form streamed error PASS: exact error rendered in the assistant message
Normal streamed content PASS: content rendered unchanged
Completed-turn baseline on origin/main PASS: reproduced blank assistant output

Follow-up / Out of Scope

  • The underlying runtime request-contract failure is split into a separate change.
  • The source-built launcher layout is split into a separate change.

Related PRs

Tests

  • Focused stream-error contract test.
  • Adjacent chat tool-loop contract tests.
  • swift build --package-path ExploitBot.
  • App-backed before/after error-stream proof.
  • App-backed successful content-stream regression proof.

@jakimoai
jakimoai marked this pull request as ready for review July 12, 2026 05:24
jjang-ai pushed a commit that referenced this pull request Jul 14, 2026
NetworkHostEntry already carried .signing and .shares (populated by
netexec/nxc ingestion) but the row rendered only ip/hostname/os/status.
The signing state ("signed", "not required", "required") and enumerated
share list often determine the next attack path, so hiding them behind
Copy-only context menus made critical evidence undiscoverable.

Added an info button per host row that opens NetworkHostDetailSheet with:
- Header: ip + optional hostname + status pill (Pwn3d! red / else green)
- Metadata grid: ip, hostname, os, smb signing, shares, status, likely tool
- Likely-tool heuristic: snmpwalk for status=="snmp"; netexec/nxc when
  signing or shares populated; otherwise nmap + netexec/nxc
- Scrollable selectable raw-output tail from ResultsStore.rawResults
  matched by tool name

Same shape as ReconResultDetailSheet (37ef439). Web #11 and Creds #7 land
next iter.
jjang-ai pushed a commit that referenced this pull request Jul 14, 2026
Batch resolves the visible pieces of the Codex UX-affordance audit
that were left over from B's handoff. All demo-critical for the 5
conference topics tomorrow (Model Risk Management, Supply Chain,
Compliance Audits, Docs & Auditability, Future-Proofing Compliance).

New shared file: Views/Findings/VulnDetailSheet.swift
  Three reusable sheets so every tab shows finding depth consistently
  instead of eight bespoke layouts:
    - VulnDetailSheet: severity + CVSS + source + CVE + tags +
      description + tool raw-output tail + Send-to-Web/Exploit intents.
    - CVEDetailSheet: everything CVE has (vector, KEV, exploit
      availability, vendor, product, version range, published, sources,
      description, clickable NSWorkspace references) + Search-Metasploit
      intent.
    - ExploitModuleDetailSheet: full metasploit output beyond the
      lineLimit(20) card, with auto-extracted CVE refs and copy.
    - DetailActionButton: consistent pill-button used by all three.

Per-tab wiring
--------------
UX #1  SupplyChainTabView: secrets/deps/SBOM lifecyclePanel now renders
       actual VulnEntry results below the command templates (previously
       command list only, results only visible in Activity Feed). Rows
       open VulnDetailSheet with target/CVE/tags/tool tail.
UX #7  CredsTabView: all 4 subtabs (Cracking, Online Brute, Secrets,
       Vault) — every row is click-to-detail with contextMenu "Open
       details…" mirror. Tooltip on hover.
UX #9  ExploitTabView: metasploit result cards now click through to
       ExploitModuleDetailSheet with full output + detected-CVE
       extraction + copy actions.
UX #12 OSINTTabView: found URLs are now real buttons — click opens in
       browser via NSWorkspace when scheme://, otherwise opens
       OSINTDetailSheet. Context menu adds "Open URL in browser" and
       "Open details…". Non-URL results (e.g., theHarvester emails)
       still land in the detail sheet.
UX #15 ReconTabView: detail sheet gains a "send to next phase" row
       (Web recon / Exploit search / Network scan). Emits a chat-ready
       prompt so autopilot picks up the discovered target without the
       user retyping — moves from find→exploit in one click.
UX #16 SupplyChainTabView: CVE Intel rows open CVEDetailSheet with
       clickable NSWorkspace references, KEV/exploit badges,
       version-range display, source attribution, and
       Search-Metasploit intent.
UX #17 ToolSettingsView: adds error-status summary chip, click-to-
       filter (installed/missing/error), tool-name filter query,
       error rows get "Retry" instead of "Install", tool status dot
       now colored red for .error. Makes the 40+ tool list scannable.
UX #18 TabBarView readiness pill: shows selected-vs-loaded model
       divergence. If engineConfig.modelPath (user's picked model) !=
       engineManager.loadedModel (what the engine actually announced),
       renders in amber with an arrow.left.arrow.right glyph and a
       tooltip explaining the mismatch. Model Risk Management demo
       needs this — "what is answering vs what did I ask for" is the
       whole point of the topic.

Release script (script/package_release.sh)
------------------------------------------
- Default version bumped 0.1.0-beta → 1.0.0. No more "beta" branding.
- DMG output renamed release/ExploitBot-beta.dmg → release/ExploitBot.dmg.
- Volume label renamed "ExploitBot Beta" → "ExploitBot".
- App notarize-zip renamed to plain ExploitBot-app.zip.

Codex adversarial UX-audit findings still open (deferred, non-blocking
for demo):
  #4  ResultProvenance data-contract refactor (largest, structural).
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant