Commit Graph
586 Commits
Author SHA1 Message Date
Said TurkseverandClaude Opus 5.5 bd97d38a99 fix(street-level): budgeted mesh sampling, in-place cones, date line, horizon
From the IC8 review of #768, each with tests that fail before:

- P1: a mesh batch no longer rebuilds the selected sequence's cones. The
  drawn cones are indexed by mesh cell; a batch that touches none of them
  is ignored and the affected cones are moved in place (no removeAll/add).
- P2: mesh sampling keeps to its 6 ms slices. Requests are taken in inside
  the budget, only cells within range of the camera are queued, the queue
  is a persistent min-heap re-ranked in one pass only after a 100 m camera
  move, and the deadline starts before any work. Coverage keeps a bounding
  box per line and densifies a line only once it comes within range. On a
  synthetic dense tile (600 x 40 points): slice median ~10 ms -> 5 ms, max
  18-30 ms -> 6.7 ms, refresh cost 11-24 ms -> 1.5-3 ms.
- P2: image cones and the photo marker are horizon-culled like the
  overview dots (one createHorizonCull in view.js, shared by all three).
- P2: a selection change recolours the previous zoom's lines still on
  screen and the old lines a cast swap keeps showing.
- P2: distances (metresBetween), line densification and ground-corner
  requests go the short way round +-180 deg (wrapLon in tileMath.js), so
  nearest ordering, cone thinning, mesh range and cast lines are right
  across the date line.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-06 15:16:57 +01:00
Said TurkseverandClaude Opus 5.5 09fc10756c fix(street-level): own the framing flight and the nearest-photo lookup
From the IC8 review of #768, each with tests that fail before:

- P1: closing a photo, switching the layer off or destroying it stops the
  1.6 s framing flight toward that photo. The flight is claimed with a
  token before flyToBoundingSphere and released by its complete/cancel
  callbacks, so cancelFraming() calls camera.cancelFlight() only while
  the flight is still Street Level's: a newer navigation flight (a search
  result, a scene) is never cancelled. Turning FOLLOW on also stops it, so
  the tween cannot overwrite the follow view.
- P2: an older nearest-photo lookup can no longer replace a newer choice.
  The core owns one AbortController per lookup, aborted by a newer lookup,
  a direct open or cone click, closing the viewer, disable and destroy;
  the signal reaches source.nearestImages through the provider, and an
  aborted lookup never surfaces as an error.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-06 15:16:57 +01:00
Said TurkseverandClaude Opus 5.5 c2f1b54d00 refactor(street-level): remove the remaining duplicated code and fakes
A duplicate scan of every module the PR adds (repeated 4-line blocks,
normalised) after gekh's dedup round left these:

- glyphs.js: the cone, ring and position glyphs share cachedGlyph(), wedge(),
  ring() and dot(). Checked with a recording canvas: all 20 draw calls keep
  the same path and fill/stroke state, so the glyphs are pixel-identical.
- panelPositionControls.js: one placeAt() for the three places that pin a
  panel's top-left corner as a fixed window.
- tiles.js: one warn() for the three background-failure logs.
- Tests: src/testSupport/streetLevelFakes.mjs holds the provider snapshot
  (uiState + presentation tests), the stand-in provider (core + controls
  tests) and one real-ray pinhole camera (view + coverage tests, which had
  each written their own). The coverage line-of-sight test still fails when
  refresh() stops passing its focus.
- qa-street-level.mjs: one waitForCoverage() for the five copies of the
  "on, drawn, settled, not key-gated" wait.

No behaviour change: unit 5862 pass, fixture gate 16/16, panel gate 5/5.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-06 14:27:58 +01:00
Huanyi Xie f4a28cce03 fix: merge TLE parser regression onto current main 2026-10-06 10:38:13 +03:00
Sameh Khamis f86d1c3296 fix: recover near-view places through the application's place search 2026-10-05 19:27:36 -07:00
Said TurkseverandClaude Opus 5.5 d70f468781 fix(street-level): harden the Mapillary tile proxy
From gekh's review of #768, each with a test that fails before:

- The routes refuse cross-site requests (sameSiteGated, as the paid
  proxies do) and the tile route has a generous per-IP limit (600/min), so
  another page cannot burn the token's quota or trip the shared 429 hold.
- At most six upstream fetches run at once; a queued request that is
  abandoned never fetches, nor does one abandoned during the disk read.
- A body past the 48 MB cap aborts the upstream request as it streams.
- Empty tiles are charged against the memory and disk budgets.
- Each disk write uses its own temp file and a retrim is skipped while a
  write of that tile is pending, so concurrent hits cannot extend a tile's
  life or fail a rename.
- Upstream refusals other than 401/403/429 answer 502.
- The tile route's success path is tested end to end (200 with the image
  layer stripped and the right headers, 204 when empty, an abandoned
  response aborts upstream); the teardown race that made this file flaky
  is gone (240 parallel runs, no failure); the QA-constant tautologies are
  replaced by a check against the real provider registry; the boundary
  group lists the modules the provider actually imports.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-05 18:35:44 +01:00
Said TurkseverandClaude Opus 5.5 5f98b871e8 fix(street-level): sequence loads, playback and nearest-photo edge cases
From gekh's review of #768, each bug with a test that fails before:

- P2: a failed sequence load clears the previous sequence's cones, resets
  the selection and can be retried; a second click while loading starts no
  second lookup. sequences.js had no tests: sequences.test.mjs now covers
  failure and retry, a superseded late answer, the cache and clearSelection.
- P2: closing a photo stops MapillaryJS sequence playback, which kept the
  hidden viewer stepping and downloading images.
- openNearest without the panel reports it instead of loading forever, and
  stops if the layer (or its provider) went off during the lookup; the
  mesh sampler's misses are bounded and forgotten with its samples; the
  sequence count is the union of ids across tiles; a provider withdraws
  only its own error, so a sequence error cannot mask KEY REJECTED; hover
  stays on (throttled) under an orbiting camera and re-picks when it
  stops; "since N days" follows the clock in a long-open tab.
- A prewarm waits for one in flight and then builds if it gave up.
- New coverage for the layer core (credits, KEY REJECTED vs REQUIRED,
  switching a provider off), the refresh wiring of the tilted-view fix and
  the draped-to-cast swap completion.
- Cleanup: loading is derived from pending; freshStreet(); tile ranking
  computes distances once and shares one path for the date line; shared
  metresBetween / whenIdle / redrawTile; one NEAREST_RADIUS_M and one
  source method list; unused contract fields, exports, getImage and the
  decoder's image branch removed; PROVIDER_COLORS lists only Mapillary.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-05 18:35:44 +01:00
Said TurkseverandClaude Opus 5.5 48276450ec fix(street-level): the panel respects restores, modes, focus and keys
From gekh's review of #768, each with a test that fails before:

- P2: the panel no longer reopens on every page load. The shell reports
  who asked for Street Level to switch on; only a user, voice or tool
  request (isExplicitLayerStateOrigin) or an opening photo opens it, and
  those automatic opens are never stored. Without the hook, a stored or
  shared "collapsed" choice wins during the restore window.
- P2: the expanded photo hides in Clean View, recording and the cockpit,
  and holds neither Esc nor Tab while hidden.
- Esc/Tab are taken only from the viewer, body or globe, not app-wide;
  FIT/FILL show the right choice while expanded; a 0x0 viewer is never
  resized (no z=NaN tile request); the expanded viewer keeps the panel's
  CSS tokens; KEY REQUIRED hides the raw "no_key" code.
- Share links carry the panel's collapsed state (ui token t); the Cyber
  theme frames the panel like its rail peers (the pinned Cyber selector
  tests now list it).
- Accessibility: radiogroups move with arrow keys and are one tab stop,
  their focus ring is no longer clipped, and the provider chips sit
  outside the disabled fieldset so the "how to add the key" hint stays
  reachable (a gated chip explains instead of toggling).
- Renders write only changed attributes (the rail's MutationObserver
  relaid out on every write); one coalesced resize per open; the legacy
  render-mode key migration is gone.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-05 18:35:30 +01:00
Said TurkseverandClaude Opus 5.5 bbe7aff242 fix(ui): floating panels stay on-screen and clear of the command dock
From gekh's review of #768, each with a test that fails before:

- P2: a floating panel whose content grows (CCTV expanded, a photo opened)
  is pulled back on-screen: every portable panel is observed and re-clamped,
  not only on window resize.
- P2: a floating header can never land under #command-dock or the voice
  pill (the rail's stacking context paints below them): clamping keeps the
  header clear of their rects on drag, restore and every re-clamp.
- A live window resize shrinks a floating window wider or taller than the
  viewport; Cyber's accordion no longer collapses docked panels when a
  floating one opens.
- The three pointer gestures share one helper (threshold, mouse/pen only,
  cancel reverts), and the header-drag threshold now has a test.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-05 18:35:30 +01:00
Said Turksever bb88a8b034 Merge branch 'main' of https://github.com/bilawalsidhu/gods-eye-view into pr/mapillary-layer
# Conflicts:
#	README.md
2026-10-05 17:06:24 +01:00
Soma Froghyar 9ba1b345e6 Merge branch 'main' into fix-tomtom-link 2026-10-05 17:48:49 +02:00
Said TurkseverandClaude Opus 5.5 0a3dc4c4ae fix(street-level): bound the tile proxy's disk cache and keep tiles' age
From the IC8 review of #768 (P2s), each with a test that fails before:

- A tile read from disk keeps its real age in memory, and the trim rewrite
  keeps the file's mtime, so nothing is served past the 24 h TTL.
- The disk cache is swept in the background at most every 15 min: expired
  tiles go, then the oldest past 1 GiB. Coverage zooms the app never
  requests (z6-10) are refused with a 400, and layer names are matched as
  own keys only.
- The tests run against a temporary cache directory (a test seam) instead
  of writing into and deleting from the developer's real .gev-cache.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-05 15:55:18 +01:00
Said TurkseverandClaude Opus 5.5 539d514adb fix(street-level): viewer lifecycle, hover cost, keyboard focus, panel lift
From the IC8 review of #768 (P2s), each with a test that fails before:

- Switching the layer off and on during the first photo download no longer
  leaves the viewer stuck on "not mounted": an outdated mount only unmounts
  an adapter nobody else holds and subscribes poses only once current, and
  the MapillaryJS adapter never builds a viewer after it was unmounted.
- Hover picks at most every 120 ms (the CCTV convention), not while the
  camera moves or a button is held, and not after the layer went off.
- Esc shrinks the expanded viewer wherever focus is (document capture), so
  the map keeps its selection, and Tab from outside returns into it.
  Closing a photo moves focus to the panel instead of dropping it on body.
- Portable panels lift only after a mouse or pen moves past the drag
  threshold: a click on a docked panel's edge or a touch swipe on its
  header no longer turns it into a floating window, and pointercancel
  saves nothing and puts the panel back.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-05 15:55:18 +01:00
Said TurkseverandClaude Opus 5.5 b1924ebb3e fix(street-level): coverage keeps its filter, selection and ground
From the IC8 review of #768 (P2s), each with a test that fails before:

- Far-side overview dots no longer show through the globe. They skip the
  depth test so they stay above Google 3D terrain, so points behind the
  horizon are now hidden with an EllipsoidalOccluder (as the cyclones layer
  does), re-culled only when the camera moves.
- The per-tile cap applies after the imagery filter: a dense tile of newer
  flat captures kept no 360° lines at all. Lookup, picking and counts use
  the drawn set.
- Lines whose ground heights were evicted, or whose terrain lookup failed,
  are cast again on the next refresh instead of staying draped for good
  (castRequested no longer latches; castAbort marks a cast in flight). The
  height cache now drops its oldest corners instead of all of them.
- A selection made or cleared while a tile's lines rebuild is applied once
  they are ready (each primitive remembers the selection it was built with).

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-05 15:55:18 +01:00
Said TurkseverandClaude Opus 5.5 f75cdf3626 perf(street-level): load the MapillaryJS stylesheet with the viewer
The 92 KB stylesheet was vendored into the main CSS, so every page load
paid for it (render-blocking) although the viewer itself is lazy. It now
comes with the lazy chunk: `import('mapillary-js/dist/mapillary.css')`
next to `import('mapillary-js')`, which Vite applies before the import
resolves, so the viewer is styled from its first frame. Main CSS in a
production build: 386 KB -> 302 KB; the MapillaryJS CSS is its own 84 KB
file. Checked in the dev server that its rules arrive with the viewer.

The vendored copy also named the wrong copyright holder and omitted the
MIT text; it is gone, and THIRD_PARTY_NOTICES.md now records mapillary-js
(MIT, Facebook, Inc. and its affiliates) and its rxjs (Apache-2.0) and
three (MIT) dependencies.

Found in the IC8 review of #768 (P2).

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-05 15:55:03 +01:00
Said TurkseverandClaude Opus 5.5 63280e22c0 fix(street-level): load the coverage in view, and name a rejected token
Tilted views: the visible box came from screen rays against the bare
ellipsoid and the 9 tiles kept were the ones nearest its centre, so a view
over high ground (Denver, 1,600 m up) or near the horizon dropped the
streets in front of the camera. At street zooms the rays now meet an
ellipsoid raised to the ground under the camera, hits beyond a range tied
to camera height are ignored, tilted views also keep the ground around the
camera, and tiles are ranked along the line of sight (nadir to screen
centre). Simulated with real Cesium ray maths over Sacramento and Denver
from 2 m to 1,000 m: the nearest visible ground and the screen centre are
always loaded.

Rejected token: Mapillary's 401/403 reached the panel as a raw "Mapillary
tiles HTTP 403" while the pill said ON, and every pan asked Mapillary again.
The proxy now answers a refusal as {keyRejected: true} and holds it for that
token (5 min), and a 429 with its Retry-After (held until then). The panel
reads KEY REJECTED, names MAPILLARY_CLIENT_TOKEN and Provider Settings,
gates its controls like KEY REQUIRED, requests nothing more, and clears the
message when the layer goes off. A rate limit keeps the drawn tiles and
refreshes once the wait is over.

Found in the IC8 review of #768 (P1 tile selection, P2 rejected token).

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-05 15:12:03 +01:00
Said TurkseverandClaude Opus 5.5 57c58a862a fix(street-level): prepare ground heights for the mesh densifying too
castLine densifies at 0.0002° when mesh heights refine it, but prepareLines
only fetched grid corners for the 0.0005° points. The finer points could
land in cells nobody fetched, so the cast came back null and the whole line
stayed draped over roofs and trees on Google 3D. prepareLines now prepares
both sets (corners are deduplicated, so the extra cost is small).

A seeded test of 300 walks with 50-300 m segments: 130 failed before, 0 now.
Found in the IC8 review of #768 (P1).

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-05 15:12:03 +01:00
Said Turksever 91d8bba4a3 Merge branch 'main' of https://github.com/bilawalsidhu/gods-eye-view into pr/mapillary-layer
# Conflicts:
#	CHANGELOG.md
#	README.md
2026-10-05 13:47:54 +01:00
Said TurkseverandClaude Opus 5.5 a5e16e9470 fix(street-level): settle loading on pan, keep zoom 0, drop late poses
Fixes from gekh's re-review of 061d269, each with a regression test that
fails on the old code:

- A pan that drops a tile still loading now settles the loading count
  (the aborted request no longer owns its key, so its `finally` never
  did), and purges the previous zoom's tiles once nothing is pending.
  LOADING no longer sticks on the status pill and chip.
- Zoom 0 (above 15,000 km) is a zoom: `zoom == null` instead of `!zoom`.
- A pose that arrives after a photo was closed is dropped, in the
  MapillaryJS adapter and in the viewer host, so the marker and sequence
  selection do not come back.
- Ground casting passes the tile's signal to the terrain request, so a
  cancelled tile stops its height batch instead of holding the queue.
- The server's in-memory tile cache honours the 24 h TTL like the disk.
- The viewer's one-time resize runs for every photo, not just the first.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-05 12:44:27 +01:00
Huanyi Xie 1c3748e3bd fix(satellites): recover TLE parsing after incomplete sets 2026-10-05 13:02:20 +03:00
daikaginza c6b43a365d fix(tiles): derive the tile origin from the configured TileJSON (#933)
`createVectorTileSource` took `tileJsonUrl` and `allowedOrigin` as independent
options, and `createOpenFreeMapSource` hardcoded both to the default host. A
caller that repointed only `tileJsonUrl` — the obvious single change — kept the
default `allowedOrigin`, so the TileJSON's absolute upstream `tiles[0]` passed
the origin check and every tile was fetched from the default host instead. No
error, no warning, and nothing reached the configured mirror.

`allowedOrigin` now defaults to the origin of `tileJsonUrl`, so repointing one
option is enough, and a mirror that serves the default host's TileJSON verbatim
fails with the existing `Invalid vector tile origin` instead of silently
bypassing itself. The origin check is unchanged: a TileJSON still cannot point
the client at a host the caller did not configure.

All three existing callers pass `allowedOrigin` explicitly
(`openFreeMap.js`, `alpr/source.js`, `traffic/flowSource.js`), and an explicit
value still overrides the derived one, so behaviour is unchanged for every
current call site.
2026-10-05 16:50:30 +07:00
Soma FroghyarandClaude Sonnet 5.5 ee7a5fb30b fix: link TomTom key setup to /keys and terms to docs.tomtom.com
Address review feedback on #903: the DATA_SOURCES terms link now points
at the actual terms page instead of a sign-in page, key-creation links
go straight to my.tomtom.com/keys, and the README table row is realigned.

Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
2026-10-05 09:34:55 +02:00
Soma FroghyarandClaude Sonnet 5.5 491a856a6b fix: point remaining developer.tomtom.com links to my.tomtom.com
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
2026-10-05 09:34:55 +02:00
Shayan khan 99f120d393 Merge branch 'main' into codex/chatgpt-realtime-oauth 2026-10-05 05:27:13 +05:00
manjunath22466 ae3612a356 fix voice debug-log privacy and docs drift
omit tool arguments unless content logging is enabled
correct the remaining current tool-count references
2026-10-05 05:46:47 +05:30
manjunath22466 0343b4495e fix the remaining voice review gaps
keep analyst counts, speech, cards, cancellation, pointer capture, tool schema boundaries, and session lifetime consistent
add the exact Alps fallback and focused regression coverage
2026-10-05 04:37:28 +05:30
manjunath22466 6858976b01 reconcile feat/voice-core with current main
preserve Bilawal's merge history, OpenAI schema-combinator startup fix, and Contacts viewport note
keep the validated voice and analyst behavior while adopting the current provider-neutral feed routes and Google token fallback
2026-10-05 04:36:14 +05:30
Said TurkseverandClaude Opus 5.5 284cd0a93d ci(street-level): run the browser gate hermetically with Mapillary fixtures
The Street Level and panel gates never ran in CI, and the live gate needs a
Mapillary token, the Mapillary network and real coverage at its test site,
so everything users see was guarded only by manual runs (which a bad token
and expired tile caches blocked twice this week).

`qa-street-level.mjs --fixtures` (npm run qa:street-level:fixtures) answers
the page's /api/mapillary/status and coverage-tile requests from generated
tiles (a street grid with flat and 360 deg sequences, half captured three
years ago; overview points from orbit) and blocks mapillary.com. Only the
six steps that open a photo are skipped. Because the data is known, the 360
deg and SINCE steps also assert that the filters remove sequences, not just
that counts do not grow. A unit test pins the fixture contents.

New CI job "Street Level browser gate": install with headless Chrome, start
a key-less dev server, run the fixture gate and qa:panel-resize. Locally,
against a dev server with every key blanked: 14/14 on three consecutive
runs, panel gate all pass.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-04 23:50:21 +01:00
Said TurkseverandClaude Opus 5.5 7ae5f52c89 test(street-level): coverage race, camera framing and panel-control tests
From the test-engineering review of #768: the rendering and panel code had
thin unit coverage (coverage.js 29% of lines, sequences/viewer/selection
~30%, cameraFollow 50%, streetLevelControls only source-text checks), and
two shipped bugs were caught only by the manual browser gate.

- coverage.test.mjs drives the real createCoverage against a fake scene and
  a source whose tile requests the test resolves. It reproduces gekh's P0 #2:
  a request superseded by retire() finishes after a newer one for the same
  tile. Without the ownership checks it drops the newer request's key and a
  refresh starts a duplicate whose lines nothing can remove (fails on the old
  loadTile, passes now). It also covers the draped-to-cast swap keeping both
  line sets on the globe until the cast one is built and clear() removing
  both mid-swap, and a tile with nothing castable not being rebuilt.
- coverageFixture.mjs encodes Mapillary-shaped coverage tiles (sequence
  lines, overview points) from lon/lat; the tests and the hermetic gate use it.
- cameraFollow.test.mjs pins the underground-camera fix: a -14,886 m sample
  is ignored for bare earth, a plausible sample wins, and following stands at
  eye height. Three of the four fail on the pre-fix module (the first reports
  "centre at -14884 m").
- streetLevelControls.test.mjs is a contract test through the real
  DataLayerManager and LayerStateCoordinator: a 360 deg click and a SINCE
  release reach the durable layer state and the encoded share link under
  Street Level's token. Against a stand-in layer it also covers the chip rule
  (darkening the last lit chip turns the layer off, provider kept on; lighting
  a dark chip is a user params request, then enable), the legend rebuilding
  on a same-count swap (review #9) and the viewer resizing only on open
  (review #8). With the old direct-to-layer routing, three of these fail,
  including both share-link tests.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-04 23:50:21 +01:00
H4ldenandClaude Opus 5.5 d43478a547 feat: add Statens vegvesen (Norway) road cameras as a CCTV pack
Norway's ~850 working road cameras load from one keyless GeoJSON request
to the agency's OGC API view of its DATEX 3.1 CCTV table, so no DATEX
account is needed. Faulty cameras are dropped, every camera sits under a
"Norway" category, and the ~135 that publish HLS play as live video
through the existing in-memory relay with the still as fallback. Frames
and manifests are pinned to each camera's own path on the agency hosts.

CCTV_VEGVESEN_ENABLED=0 disables the pack, CCTV_VEGVESEN_MAX_SOURCES
caps it (nearest the largest cities first) and CCTV_VEGVESEN_VIDEO=0
keeps stills only. Attributed under NLOD 2.0.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-04 22:23:16 +02:00
devv-shayan 467c4e928d test(voice): match API-key realtime auth response 2026-10-04 23:03:09 +05:00
Sameh Khamis a2f61d4def feat: load Google 3D with short-lived server tokens
Without a browser key, the app asks its server for a short-lived Google
access token (GET /api/google/tiles-token, answering accessToken and
expiresAt) and loads Photorealistic 3D Tiles directly with it as a
header. A tile refused for an expired token gets a renewed one and is
retried once. A server that does not offer tokens changes nothing: the
app continues through ion or the keyless globe as before.
2026-10-04 10:49:06 -07:00
devv-shayan 8f5710e7cb fix(voice): track OAuth login lifecycle and storage readiness 2026-10-04 17:24:30 +05:00
devv-shayan 6c88b78140 Merge upstream main into ChatGPT Realtime OAuth 2026-10-04 12:29:54 +05:00
Bilawal Sidhu e7cb9b02b5 Merge main into feat/voice-core
Keeps the voice-core prompt rules and adds main's world-query and
vessels-vs-visuals rules; the tool catalog wraps the deixis-aware runner.

Also fixes two things the branch had before the merge:
- Live voice could not start: OpenAI rejects track_entity's top-level
  anyOf. Model-facing tools now omit top-level combinators; the runner
  and benchmark still require a query or a referent.
- The Contacts-centred analyst answer dropped the viewport coverage note.
2026-10-03 23:30:02 -07:00
Sameh Khamis 60644c5f5d feat: name live feed routes and sources for what they serve
The flight, military and vessel feeds were named after the providers the
bundled server uses (/api/opensky, /api/adsblol/mil, /api/ais-live and
their track routes; createOpenSkySource, createAdsbLolSource,
createAisStreamSource). Any server should be able to answer a feed from
its own provider, so the feeds are now:

  /api/flights   /api/flights/track?icao24=
  /api/military  /api/military/track?hex=
  /api/vessels   /api/vessels/track?mmsi=

with createFlightSource, createMilitarySource and createVesselSource. The
old routes are removed. Each server names its provider in X-Feed-Source
(the bundled server sends OpenSky Network, adsb.lol and AISStream, so its
attribution is unchanged) and the military feed's cache state and age in
X-Feed-Cache and X-Feed-Age-Ms. Sources report the provider the server
names and fall back to the feed's name.
2026-10-03 23:10:32 -07:00
Sameh Khamis e8e49a49e0 feat: ask for vessels by area
The vessel source accepts an optional area and sends it as lat, lon and
radius_km. The vessel layer asks for the area the camera shows (centered
on the middle of the screen, sized by camera height), or every vessel
from high enough up, and asks again when the view moves or zooms by a
quarter of that radius. vessels_in_area names its area too. Servers that
hold every vessel may ignore the area; callers still keep only what is
inside it.
2026-10-03 22:44:45 -07:00
Sameh Khamis ee05e922c0 feat: export the globe panel for MCP servers
MCP servers that compose Core's tools can now serve the globe panel:
@gods-eye-view/core/tools/panel exports createGlobePanelResource,
panelRuntime, GLOBE_PANEL_URI and PANEL_BASE. The local MCP server uses
the same entry point.
2026-10-03 21:17:47 -07:00
Sameh Khamis f752b5eea1 fix: resolve bundled data URLs when used, not at import
The submarine cable source and the infrastructure data URLs built their
asset URLs from import.meta.url while the module loaded. Importing the
tool services therefore failed in runtimes without a usable module URL,
even for tools that never read those files. Build the URLs when they are
read; Vite still rewrites them in browser builds.
2026-10-03 18:47:31 -07:00
Said TurkseverandClaude Opus 5.5 5a7ce5785e Merge upstream/main (v0.2.1) into pr/mapillary-layer
Brings in 123 commits from main, including the v0.2.0 and v0.2.1 releases
(MCP server and tool catalog, server security hardening, build hygiene).

Conflicts:
- package.json: kept main's script order and added this PR's
  qa:panel-resize and qa:street-level; exports keep both sides (main's
  tools/view entries and this PR's street-level and Mapillary entries).
- src/tooling/viteBuild.test.mjs: kept this PR's Mapillary-token define
  assertion and main's new wildcard-host allowedHosts assertions.
- CHANGELOG.md: main now has release sections; this PR's entries go under
  [Unreleased].

Checks after the merge: doctor, format:check, check:boundaries,
layer-token:check (28 published, 1 new: street-level 0), npm test (5726
pass), build, the Windows job's focused tests (70 pass), qa:panel-resize,
and the keyless street-level gate. The new CSP allows Mapillary (https:
connect/img, blob workers). The keyed gate stops at coverage locally
because the dev .env Mapillary token is invalid (upstream 403), not because
of the merge.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-03 13:29:32 +01:00
Sameh KhamisandClaude Opus 5.5 b33cae4b74 Merge main into #873
Resolve the CHANGELOG and CURRENT-STATE conflicts: the PR's entries go under
Unreleased, after the 0.2.0 release.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-02 21:18:15 -07:00
Sameh KhamisandClaude Opus 5.5 9c87fabf88 build: ship each bundled data pack once
loadBundledJson's callers passed a literal JSON import as a Node fallback,
which made the production build emit a JavaScript copy of every pack beside
the JSON the browser fetches. The loader now reads file: URLs itself under
Node (through process.getBuiltinModule, synchronously, as promptly as the
cached import it replaces), and the callers pass only the URL. On a Node too
old to provide getBuiltinModule it says that Node 24.14 or newer is needed.
dist/ drops from 54 MB to 42 MB. A test keeps app code from importing a pack
as a module.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-02 21:08:11 -07:00
Sameh KhamisandClaude Opus 5.5 0646630dec fix(server): let the CSP load the Nepal event's embedded media
The Content-Security-Policy blocked the YouTube player API, the Facebook
video SDK and X's post widget that src/data/bhoteKoshiEmbeddedMedia.js
loads, along with their players and posts in frames. script-src now allows
exactly those three script origins and a new frame-src allows YouTube,
youtube-nocookie, Facebook and X embeds. A test ties the policy to the
frames the media module builds and pins the allowed script origins.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-02 20:21:23 -07:00
Sameh KhamisandClaude Opus 5.5 913d45bc07 fix(server): keep cost throttles on for positive fractional limits
A limit such as 0.5 floored to 0 and disabled the throttle, although only an
explicit 0 is meant to. Positive values below 1 now count as 1.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-02 20:17:21 -07:00
Sameh KhamisandClaude Opus 5.5 9b4ccbff62 fix(server): gate Google text search against cross-site requests
/api/google/text-search spends the same Google quota as nearby-places but
was not behind the cross-site gate. It now refuses cross-site requests
before calling Google, and a test covers both paid Google routes.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-02 20:16:39 -07:00
Sameh KhamisandClaude Opus 5.5 e6f1dd70a1 fix(server): apply the Host check before the app's own routes
Vite installs its Host check after the middleware plugins add in
configureServer, so the provider and /api routes answered any Host,
including a DNS-rebinding name. hostCheckPlugin runs first on the dev and
preview servers and applies Vite's rule with the same allowed hosts: IP
addresses, localhost and *.localhost, the configured server host, and the
listed names. A test drives a real Vite server with an unlisted Host.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-02 20:15:54 -07:00
Sameh KhamisandClaude Opus 5.5 81513f9aae fix(mcp): refuse panel requests to routes reached through a dot suffix
The server routes a mounted path when a dot follows it, so /mcp.json reached
the MCP endpoint while panel_request refused only /mcp and /mcp/...; the same
held for the provider-settings, credential and model routes. The refusals now
match a dot after the route as well. A regression test drives panel_request
against a real Vite server.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-02 20:10:47 -07:00
Sameh KhamisandClaude Opus 5.5 6cdd6c8af9 refactor(server): share one proxy-header check across the local gates
localRequestGate.mjs owns the reverse-proxy header list and hasProxySignals;
Provider Settings, the cross-site gate and /mcp all use it instead of two
copies of the same nine names. The changelog credits the cross-site gate.

Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
2026-10-02 20:02:48 -07:00
bbf4976f5e fix(server): keep debug-log timestamps server-owned and send nosniff
The voice debug log writes the server's timestamp after the client's record,
so a record can no longer replace loggedAt. The dev and preview servers also
send X-Content-Type-Options: nosniff with their shared browser headers.

From the findings in #710.

Co-authored-by: Sunil <[email protected]>
Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
2026-10-02 20:00:11 -07:00
98b8c6c581 fix(server): keep the Host-header check when binding to all interfaces
Binding HOST=0.0.0.0 no longer sets Vite's allowedHosts to true. The dev and
preview servers accept IP addresses, localhost and the LAN hostnames listed
in GEV_ALLOWED_HOSTS; suffix and wildcard entries are ignored. The
configuration factory takes the resolved list as an input, so build/vite.js
still reads no environment.

Ported from #97 to the current build/vite.js layout. Fixes #21.

Co-authored-by: Sameh Khamis <[email protected]>
Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
2026-10-02 19:59:02 -07:00