Skip to content

Tags: Zipstack/unstract

Tags

v0.177.6

Toggle v0.177.6's commit message

Verified

This commit was created on GitHub.com and signed with GitHub’s verified signature.
UN-3185 [FIX] Restore global Prism for prismjs add-ons (Prompt Studio…

… detail + HITL blank page) (#2135)

* UN-3185 [FIX] Restore global Prism for prismjs add-ons (Prompt Studio detail + HITL blank page)

The Vite production build tree-shakes the bare `import "prismjs"` in
CombinedOutput.jsx (a side-effect-only import with no used bindings), so
nothing installs the global `Prism` that `prismjs/components/prism-json`
and the line-numbers plugin reference. Those add-ons then throw
`ReferenceError: Prism is not defined` at module evaluation, crashing the
Combined Output viewer shared by the Prompt Studio detail page and the
HITL / manual-review page (both render blank). The old CRA/webpack build
did not tree-shake it, so the regression only surfaces on the Vite build.

Add a dedicated prismSetup module that imports Prism core with a *used*
binding (survives tree-shaking) and pins it on globalThis, and import it
before the add-ons in CombinedOutput so the global is guaranteed present
by the time the add-on modules evaluate.

Verified against a production `vite build`: the emitted chunk now runs
`globalThis.Prism = <core>` immediately before `Prism.languages.json = ...`.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* UN-3185 [FIX] Simplify globalThis guard in prismSetup (review)

Drop the always-true `typeof globalThis !== "undefined"` check — globalThis
is universally available in any ESM/Vite target. Addresses Greptile review.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* UN-3185 [FIX] Install global Prism unconditionally + correct rationale (review)

Address PR review:
- silent-failure-hunter: a `!globalThis.Prism` guard could leave a different,
  pre-existing Prism in place, so the add-ons extend one instance while
  JsonView's highlightAll() reads another -> JSON silently unhighlighted.
  Assign unconditionally so the global is provably our core instance.
- comment-analyzer: the prior comment blamed evaluation order yet relied on it
  to justify the fix. Rewrite: relying on prismjs core's self-install is
  unreliable under code-splitting; the explicit globalThis assignment (from
  first-party code imported before the add-ons) is the deterministic fix.

Rebuilt: emitted chunk runs `globalThis.Prism = <core>` immediately before
`Prism.languages.json = ...`.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* UN-3185 [FIX] Install global Prism eagerly at app entry (fixes HITL too)

Verified against the actual on-prem image (built with the manual-review
plugin): the per-component prismSetup import did NOT fix it. Because
manual-review's ResultEditor also imports `prismjs/components/prism-json`,
Rollup hoists that add-on into a SHARED lazy chunk (PdfViewer), separate
from CombinedOutput's chunk where prismSetup ran — with no ordering
guarantee between two lazy chunks, so the add-on still evaluated before the
global was installed. My local OSS build masked this: with no manual-review
plugin, prism-json wasn't shared and stayed in CombinedOutput's chunk.

Fix: import prismSetup EAGERLY from index.jsx so `globalThis.Prism` is
installed at bootstrap, before any lazy chunk (including the shared add-on
chunk) can load. Robust regardless of how Rollup hoists prism-json.

Reproduced the shared-chunk hoist locally (two independent lazy prism-json
importers) and confirmed: prism-json lands in its own lazy chunk while
globalThis.Prism stays in the eager entry <script type=module>, so the
global is always installed first. Fixes both the Prompt Studio detail page
and the HITL review page.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>

v0.177.5

Toggle v0.177.5's commit message

Verified

This commit was created on GitHub.com and signed with GitHub’s verified signature.
UN-3185 [FIX] Deterministic CSS cascade (single bundle) + asset gzip …

…+ Edit LLM Profile modal layout (#2128)

* fix(frontend): bundle all CSS into one file to fix lazy-load cascade order

Code-splitting (#2114) injects per-route CSS at navigation time, so
equal-specificity rules across components resolve in load order — breaking
the Edit LLM Profile form and Execution Logs header layout (UN-3185).
cssCodeSplit:false restores a single deterministic stylesheet; JS splitting
is unaffected.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015DsoHbMN7kTTWwcVC6NNkg

* perf(frontend): gzip static assets (css/js/fonts), not just html

nginx `gzip on` only compresses text/html by default, so JS/CSS shipped
uncompressed. Add gzip_types so the single CSS bundle (~341KB -> ~57KB) and
all JS chunks compress on the wire — offsets the cssCodeSplit:false upfront
cost and speeds every asset.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015DsoHbMN7kTTWwcVC6NNkg

* fix(frontend): clean up Edit LLM Profile modal padding & drop dead scroll classes

Remove settings-body-pad-top + add-llm-profile-scroll-root from the form root
(the shared overflow-y:auto added a nested scroll context that #2119 only
patched over). The sticky footer now pins directly to .conn-modal-col. Replace
the dead override with a :has()-scoped padding so .conn-modal-form-pad-left is
adjusted for this panel alone — sibling settings panels and the connector
dialog are untouched.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015DsoHbMN7kTTWwcVC6NNkg

* perf(frontend): gzip_proxied any so assets compress behind the LB

nginx skips gzip for proxied requests by default (gzip_proxied off); the GKE
load balancer adds a Via header to every request, so static assets shipped
uncompressed despite gzip_types. Verified gzip works direct-to-pod.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015DsoHbMN7kTTWwcVC6NNkg

* fix(frontend): remove inner scrollbar on Edit LLM Profile modal

The settings row is hard-fixed at 800px, forcing the form's column
(.conn-modal-col) to scroll once Advanced Settings expands. Let the row size to
the form so the modal grows instead. Scoped via :has to the LLM profile panel —
the other settings panels and the connector dialog share .conn-modal-row/-col
and are untouched (so the shared class stays; it is not unused).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015DsoHbMN7kTTWwcVC6NNkg

* docs: tighten code comments in CSS/vite/nginx changes

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015DsoHbMN7kTTWwcVC6NNkg

* style: drop comments from AddLlmProfile.css changes

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015DsoHbMN7kTTWwcVC6NNkg

* fix: drop pre-compressed font types from gzip_types

WOFF2 (Brotli) and WOFF (zlib) are already compressed; re-gzipping wastes
CPU for no size gain. application/font-woff also never matches nginx's
mime.types (.woff -> font/woff), so it was a no-op.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015DsoHbMN7kTTWwcVC6NNkg

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

v0.177.4

Toggle v0.177.4's commit message

Verified

This commit was created on GitHub.com and signed with GitHub’s verified signature.
UN-3185: Pin LLM profile form submit button as a sticky footer (#2119)

* UN-3185: Pin LLM profile form submit button as a sticky footer

In the prompt-studio Settings modal, the LLM-profile Add/Edit form put its
submit button (Update/Add) as the last element in normal flow inside a fixed
800px scroll column. When the form is tall — e.g. Advanced Settings expanded —
the button flowed past the modal's visible area and appeared to overflow.

- Make the button a sticky footer (position: sticky; bottom: 0) with a solid
  themed background so it stays pinned at the bottom of the scroll column and
  fields scroll beneath it.
- The form root's .settings-body-pad-top declared overflow-y: auto with no
  bounded height — a dead nested scroll context that would stop the sticky
  footer from pinning to the real scroller (.conn-modal-col). Override it back
  to visible for this form (scoped via .add-llm-profile-scroll-root).

Verified the sticky pinning against the real CSS rules; build green, biome clean.

* Apply suggestion from @greptile-apps[bot]

Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
Signed-off-by: Jaseem Jas <89440144+jaseemjaskp@users.noreply.github.com>

---------

Signed-off-by: Jaseem Jas <89440144+jaseemjaskp@users.noreply.github.com>
Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>

v0.177.3

Toggle v0.177.3's commit message

Verified

This commit was created on GitHub.com and signed with GitHub’s verified signature.
UN-3185: Defer heavy chunks on login route + cache fingerprinted asse…

…ts (#2114)

* UN-3185: Defer heavy chunks on login route + cache fingerprinted assets

Fix 1 (nginx.conf): serve content-hashed /assets/* with a 1y immutable
Cache-Control while keeping index.html and config/runtime-config.js
non-cached, so repeat visits stop re-fetching the whole bundle.

Fix 2: stop the unauthenticated /landing page from eagerly downloading
the full app. Convert OSS route pages and enterprise plugin route
elements to React.lazy behind a single <Suspense>, and lazy-load the
app shell (PageLayout/FullPageLayout) which transitively pulled the
PDF viewer, charts and lookup-studio onto /landing.

New helper src/helpers/pluginRegistry.js (lazyPlugin) defers plugin
chunks to navigation; OSS builds resolve the stub and fall back to
NotFound, so absent-plugin routes 404 harmlessly as before.

Measured /landing script requests: 201 -> 93; pdf-vendor, recharts and
Monaco no longer load until navigated to. OSS-parity build (no
src/plugins) verified.

* UN-3185: Address review — preserve security headers, tighten plugin-absent check

- nginx: drive Cache-Control from a $uri map at server scope instead of
  per-location add_header. Location-level add_header replaces (not merges)
  the inherited server headers, which dropped X-Content-Type-Options/
  X-Frame-Options/Referrer-Policy/CSP from /assets, index.html and
  runtime-config.js. Now both locations carry no add_header and inherit all
  security + cache headers.
- pluginRegistry: only treat the build-time stub ('Optional plugin not
  available') as plugin-absent; rethrow transient chunk-load failures of a
  shipped plugin instead of masking them as NotFound.
- useMainAppRoutes: gate the OnboardProduct wrapper on PRODUCT_NAMES.unstract
  (the value passed as type) rather than the map being non-empty.

* UN-3185: Extract shared lazyNamed helper (dedupe lazy-route boilerplate)

Pull the repeated 'lazy a named export' pattern into src/helpers/lazyNamed.js
and use it in Router.jsx and useMainAppRoutes.js instead of inline
.then((m) => ({ default: m.X })) / a per-file 'named' helper. Behaviour is
identical; /landing chunk count unchanged. Addresses cloud-PR review feedback
about the duplicated helper (the two route hooks import the same util).

* UN-3185: Add ErrorBoundary around lazy routes; validate lazyNamed export

- Wrap the route <Suspense> in Router.jsx with the existing ErrorBoundary and
  a reload-prompt fallback. lazyPlugin/lazyNamed rethrow non-stub failures
  (e.g. a transient chunk-load blip), and App rendered <Router/> with no
  boundary — so such a failure would unmount the tree to a blank screen.
  The boundary now contains it and offers a reload (which re-fetches the chunk).
- lazyNamed: throw a descriptive error when the requested named export is
  missing instead of handing React.lazy { default: undefined } (opaque error).

Addresses greptile P1 (no ErrorBoundary) and CodeRabbit (lazyNamed validation).

* UN-3185: Scope route error handling, validate lazyPlugin export, drop dead guard

Review round 3:
- lazyPlugin: validate the resolved export (mirror lazyNamed). A shipped plugin
  whose named export was renamed/removed now throws a descriptive error instead
  of handing React.lazy { default: undefined }; isPluginAbsent doesn't match it,
  so it re-throws to the ErrorBoundary rather than masking as NotFound.
- ErrorBoundary: support resetKeys — clear the error when a key changes (e.g.
  location), so navigation recovers without a full reload.
- New LazyOutlet (content-scoped <Suspense> + nav-resettable ErrorBoundary +
  <Outlet/>); PageLayout/FullPageLayout use it instead of a bare <Outlet/>.
  Per-page load spinners and chunk-load failures now stay in the content area
  with the shell mounted; the app-wide boundary in Router.jsx remains the
  backstop and is now also location-reset. Fixes the blast-radius / no-recovery
  and shell-blanks-on-first-nav issues from the single top-level boundary.
- useMainAppRoutes: remove the now-dead 'ReadOnlyReviewPage && !ReviewLayout'
  warning — with lazyPlugin both are always truthy so it could never fire; the
  route degrades to NotFound if manual-review is absent.

Build green (with and without src/plugins); biome clean.

* UN-3185: Use globalThis.location.reload() in RouteLoadError (SonarCloud S7764)

Prefer globalThis over window for the reload handler, matching existing
globalThis.location usage in the codebase (e.g. SideNavBar). Clears the only
open SonarCloud issue on this PR (javascript:S7764, minor code smell).

* UN-3185: Auto-reload once on chunk-load errors (stale chunk after deploy)

The route ErrorBoundary already catches a rejected dynamic import() (a
<Suspense> alone only handles the pending state). Add chunk-error handling so
the common production trigger — a stale hashed chunk after a redeploy (client
requests a filename the CDN no longer serves) — auto-recovers:

- isChunkLoadError() detects the failed-dynamic-import error across browsers.
- handleRouteError() (wired as onError on both the app-wide boundary in
  Router.jsx and the content-scoped one in LazyOutlet) reloads ONCE to pick up
  fresh chunk hashes, guarded by a sessionStorage timestamp so a genuinely-gone
  chunk falls through to the manual Reload fallback instead of looping.
  Non-chunk render errors are never auto-reloaded.

This covers the outermost lazy route elements too (e.g. FullPageLayout for
verticals, OnboardProduct for llm-whisperer), which render under the Router
boundary.

v0.177.2

Toggle v0.177.2's commit message

Verified

This commit was created on GitHub.com and signed with GitHub’s verified signature.
[MISC] Reject no-op OSS releases when there are no new commits (#2117)

* [MISC] Reject no-op OSS releases when there are no new commits

create-release.yaml computed and published a new tag even when the branch had
no commits beyond the last release, producing empty releases (e.g. v0.177.1
points at the exact same commit as v0.177.0).

Add a guard after "Fetch base release version": use the compare API's ahead_by
(the workflow has no local checkout) to count commits on the branch beyond the
latest release tag. If zero, abort with a clear error (dry-run only warns).
Works for main and hotfix branches alike.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* [MISC] Harden no-op guard: validate ahead_by, fail loudly on unexpected value

PR review (Greptile + internal): `[[ "$AHEAD" -eq 0 ]]` mishandled a non-integer
ahead_by — an empty value evaluated true ([[ "" -eq 0 ]]) and would wrongly
reject a legitimate release, and a literal "null" aborted with a cryptic
"integer expression expected" / unbound-variable error.

Use `--jq '.ahead_by // empty'`, fail loudly if the compare API call fails, and
require ahead_by to match ^[0-9]+$ (error out otherwise) before a string compare
against "0". A transient/auth/shape error now fails the step with a clear
message instead of being mistaken for "no new commits".

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>

v0.177.1

Toggle v0.177.1's commit message

Verified

This commit was created on GitHub.com and signed with GitHub’s verified signature.
[MISC] Surface LLMWhisperer adapter env knobs in workers sample.env (#…

…2113)

[FIX] Surface LLMWhisperer adapter env knobs in workers sample.env

Text extraction now runs in-process inside the file-processing workers
(via the SDK x2text adapter), not in a separate tool container. The
ADAPTER_LLMW_* knobs are read with os.getenv from the worker process, so
they must be present in the worker env; previously they were only set on
the backend/prompt envs and silently fell back to SDK defaults.


Claude-Session: https://claude.ai/code/session_01SBT1USHvaBLqEi9Xd8NoBP

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

v0.177.0

Toggle v0.177.0's commit message

Verified

This commit was created on GitHub.com and signed with GitHub’s verified signature.
[MISC] Surface LLMWhisperer adapter env knobs in workers sample.env (#…

…2113)

[FIX] Surface LLMWhisperer adapter env knobs in workers sample.env

Text extraction now runs in-process inside the file-processing workers
(via the SDK x2text adapter), not in a separate tool container. The
ADAPTER_LLMW_* knobs are read with os.getenv from the worker process, so
they must be present in the worker env; previously they were only set on
the backend/prompt envs and silently fell back to SDK defaults.


Claude-Session: https://claude.ai/code/session_01SBT1USHvaBLqEi9Xd8NoBP

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

v0.176.5

Toggle v0.176.5's commit message

Verified

This commit was created on GitHub.com and signed with GitHub’s verified signature.
[HOTFIX] Raise URLValidator max_length to 8192 for long S3 presigned …

…URLs (#2111)

v0.176.4

Toggle v0.176.4's commit message

Verified

This commit was created on GitHub.com and signed with GitHub’s verified signature.
UN-3621 [HOTFIX] Structure tool no longer crashes when single-pass ex…

…traction returns a non-object output (#2110)

UN-3621 [FIX] Guard structure pipeline against non-dict single-pass output

Single-pass extraction can return a top-level JSON array (e.g. a truncated/
runaway LLM response that hit its output-token cap). The parsed `output` is
then a list, and _handle_structure_pipeline called `.values()` on it
unconditionally, raising an opaque `AttributeError: 'list' object has no
attribute 'values'` that failed the file with no actionable signal.

Guard the output shape: if it isn't a dict, return a clear ExecutionResult
failure naming the likely cause instead of crashing, and stop the malformed
payload from flowing downstream as a success.

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

v0.176.3

Toggle v0.176.3's commit message

Verified

This commit was created on GitHub.com and signed with GitHub’s verified signature.
[FIX] Revert djangorestframework 3.15.2 → 3.14.0 to avoid UniqueToget…

…herValidator errors (#2098)

[FIX] Revert djangorestframework 3.15.2 -> 3.14.0 to unblock staging

The DRF 3.15.2 bump (#2087) regressed rc.343. DRF 3.15 auto-derives
multi-field UniqueTogetherValidators from model UniqueConstraints, which
3.14 only did for legacy unique_together. Two breakages followed for every
ModelSerializer(fields="__all__") over a model using Meta.constraints:

1. Server-set constraint fields (e.g. organization) -> "<field>: required"
   on create. Partially patched by #2092 for the 5 org-attached models.
2. Client-supplied constraint fields (TableSettings, ProfileManager,
   agentic table settings, lookups) -> "...must make a unique set" raised at
   is_valid(), short-circuiting the views' intended
   `except IntegrityError: raise DuplicateData(<friendly>)` path. This both
   replaced the friendly message and moved the error from a top-level
   `detail` string into nested `non_field_errors`, which the frontend does
   not surface -> silent failures (e.g. duplicate LLM profile name, table
   settings no longer editable after first save).

Pin back to 3.14.0 to restore the known-good behaviour across the whole
unique-constraint class at once. The CVE-2024-21520 XSS patch carried by
3.15.2 is intentionally deprioritized; the 3.15 upgrade will be reattempted
later with a serializer-level fix (drop auto-derived uniqueness validators).

Reverts only the DRF entry from #2087; other batched bumps untouched. The
org `editable=False` changes (#2092) remain correct no-ops under 3.14 (org
is set server-side in save() from UserContext), so no rollback is needed there.


Claude-Session: https://claude.ai/code/session_01G8hAHc4HUo42zY1g9LAjKu

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>