Commit Graph
3178 Commits
Author SHA1 Message Date
quanticsoul4772 8b363d39f6 Merge branch 'main' into upstream/traffic-paired-flow-density 2026-11-03 06:20:51 -08:00
quanticsoul4772 1c70c6d042 Merge branch 'main' into upstream/datacenter-ground-sampling 2026-11-03 06:20:43 -08:00
Manjunath 013cfdf91d fix(awareness): keep range pulses out of picking and in the style palette
Review follow-up for the sonar pulses (the non-blocking items).

Unpickable pulses: the three pulse lines were polyline entities, which
Cesium always picks, so near-miss and touch picks around them became
unowned hits. They are now drawn by the pulse disc's shader, which
already drew the gradient and is built with allowPicking false. Each
line keeps the polyline outline material's look: 3 CSS px wide with a
1 px dark outline in the ring's colours, measured from the screen-space
gradient of the disc's radius times czm_pixelRatio, as Cesium's contour
material does. Without derivatives only the gradient band is drawn.

Disc follow: with the lines on the disc, it is rebuilt after the subject
moves 500 m instead of 2 km, so the pulses stay concentric with the
ring. A resting disc is hidden and not rebuilt; it catches up when the
pulses wake.

Camera below the disc: the disc sits 2.5 km above the ellipsoid with
faceForward, so from a lower camera it swept across the sky. It is now
hidden while the camera is below that height.

Echo palette: echo brackets keep the pulse blue on the default
detection style and take the style's line colour under NVG, FLIR and
CRT.

Echo test: the detection renderer's echo maths moves into
awarenessEchoBracket and awarenessEchoColor in src/awarenessEcho.js,
tested directly instead of running a regex slice of detection.js
through vm, and the one-sided spread assert is now two-sided.

The positions reuse parameter of awarenessRangeRingPositions and the
pulse segment count had no callers left and are removed.
2026-11-03 19:44:09 +05:30
Manjunath 2aef67197c Merge main into the voice tilt branch 2026-11-03 19:40:13 +05:30
Manjunath 40d8839589 fix(voice): localize the area, imagery and OSM cards and tidy OSM Places
From review:
- Card lines the actions write in English (area basis, part counts, merged
  duplicates, OSM caveats, the imagery window) are shown in the UI
  language through the analyst card's note path; the model still gets the
  English. One OSM place reads "1 hospital".
- Same-named clarification choices show their region code (WA, DC).
- OSM Places shows as unavailable once the server says no Overpass is
  configured, and its points are depth-tested past 2.5 km so they never
  show through the globe.
- find_imagery puts the operator's sources and mode back when a box is
  refused, and a voice search cancelled mid-flight leaves "Search
  cancelled" instead of an empty strip.
- The README lists OSM Places with the other Utilities.
2026-11-03 19:36:48 +05:30
Manjunath 34647758fc test(osm): cover the place-search client and the follow-up handoff
From review:
- The browser client for /api/osm/features gets its own tests (request
  shape, abort, network failure, coded refusals), and a 200 whose body is
  not JSON is now a failure instead of zero places.
- An end-to-end test runs resolve_area, osm_query and an analyst follow-up
  through the real runner and checks the follow-up keeps the area; it fails
  with the wiring reverted.
- The Realtime token test pins Overpass off and compares the full tool
  schemas again instead of names taken from the environment.
2026-11-03 19:36:40 +05:30
Manjunath 8366803c32 perf(analyst): yield area scans on a time budget
From review: a row inside a large area pays a ray cast over every edge, so
20,000 rows against a 6,000-vertex outline made ~200 ms long tasks. The scan
now also yields once ~8 ms have passed, checked every 256 rows. An ambiguous
region refusal also tells the model the way back: resolve_area with the
picked candidateId, then analyst_query over that area.
2026-11-03 19:36:32 +05:30
Manjunath 207373389a fix(voice): resolve areas per view, without accents and per natural place
From review:
- An answer the view picked ("Springfield" over Illinois) is reused or
  shared only within the same ~2° view cell, so asking again elsewhere no
  longer gets the first state's area.
- Admin names compare with accents folded on Latin letters only: "Quebec"
  matches "Québec" and "Cordoba" asks between Argentina and Spain, while a
  Thai or Devanagari mark still tells names apart.
- A Natural Earth name used by several features is one place with every
  part when they touch (the Great Rift Valley, the Lau Group across 180°)
  and a question when they are far apart (Flores); natural places can now
  be candidates.
- The AREAS rule and the area tool descriptions are tighter, leaving a
  ~270-character margin under the 55,500 prompt ceiling, and resolve_area
  no longer names osm_query when that tool is not offered.
2026-11-03 19:36:25 +05:30
Manjunath fb74354d30 fix(osm): gate place search cross-site and share its slots fairly
From review:
- /api/osm/features now refuses cross-site browser requests like the other
  quota-bearing routes, so a page elsewhere cannot spend the operator's
  Overpass. SECURITY.md lists the route, its limits and its disk cache.
- One client runs one search at a time and two run at once in all, instead
  of one global slot any single client could hold.
- A search gets 45 s across every configured endpoint in turn, answered 504
  when it runs out.
- A cache write no longer waits on the disk prune; prune() waits until the
  cache is idle for callers that need it.
2026-11-03 19:36:16 +05:30
Manjunath 5c74b860b7 Address review: zoom, drag, clearance and pitchDeg for voice tilt
- A zoom during a pivoting tilt now scales the range the tilt holds,
  as it already did for orbit, instead of being undone on the next
  frame while reporting success. The zoom limits no longer move a view
  the wrong way: a zoom in leaves a view closer than 80 m in place, and
  a zoom out leaves an orbit beyond 5,000 km where it is.
- A grab, touch or wheel on the map while a voice fly-to is in flight
  drops the motion queued behind it. Cesium lands the flight anyway,
  so the orbit or tilt would otherwise take the camera back. Chained
  fly-tos in one reply keep the earliest snapshot.
- A pivot tilt toward the horizon stops lowering the camera at 150 m
  above the pivot and tilts the rest in place, so a close view no
  longer ends inside tiles or a hillside.
- pitchDeg is an absolute angle. A direction that contradicts it is
  refused, judged from the pitch the swing starts at, and the tool
  description says so.
2026-11-03 19:35:02 +05:30
Said Turksever 895ef536ca docs: changelog fragment for #1522 2026-11-03 13:50:45 +00:00
Said Turksever e6a6ba0126 fix: keep the globe on screen when zooming out from a tilted view
Cesium zooms a shallow view out by backing along the cursor ray with
heading and pitch held fixed, and GEV set no zoom ceiling. Past ~700 km
the limb drops below the fixed pitch, the steps grow with height, and
the camera flies off into space.

Cap user zoom at 40,000 km (whole-Earth view; flights are not limited)
and, above 300 km during a user wheel, pinch or right-drag zoom-out,
ease the pitch down to at least 8 degrees below the Earth's limb. The
clamp yields to disabled inputs, tracked transforms and camera flights.
2026-11-03 13:50:16 +00:00
Manjunath 0c025987ef fix(awareness): bound the range ring pulses so a parked view goes quiet
Review follow-up for the sonar pulses.

Bounded repaint: the pulses used to ask for a frame every 80 ms from a
setInterval for as long as a subject was shown, so a parked view
repainted at 12.5 fps indefinitely. They now run for 9 s (three ring
flashes) after a selection or a camera move, fading in and out over
1.2 s, then rest: the ring stands on screen at full strength, the pulse
lines and gradient disc are hidden, echoes stop, and nothing asks for
frames. A moving subject does not wake them, since a flying subject
moves on every refresh. The lifecycle's existing camera pose signature
wakes them.

No beat with the Cyber sonar: the setInterval is replaced by a
postRender-armed setTimeout on CYBER_SONAR_RENDER_INTERVAL_MS, as in
cyberSonarScene. Every drawn frame, whoever asked for it, re-arms the
request 80 ms later, so requests never land inside the detection
lane's 80 ms gate.

Reduced motion mid-session: a change listener on the media query
rebuilds the visual, and the same-subject refresh path rebuilds when
the preference no longer matches. The listener is removed in
clearVisual.

Readable edge: restOpacity goes from 0 to 0.4, so the 250 km ring stays
visible between flashes, in screenshots and for low-vision viewers.

The echo field takes an optional level so echoes rest with the pulses.
CURRENT-STATE and the changelog fragment describe the bounded run and
the dimmed ring.

Tests: render requests stop after the bound and stay stopped for a
minute; a camera move seen by the lifecycle wakes them for another
bounded run; a foreign frame postpones the next request to 80 ms after
it; reduced motion flipped by event and by a later refresh; the ring
never drops below restOpacity; echoes scale with the level; the level
fades in, holds, fades out and keeps its value when woken.
2026-11-03 19:17:03 +05:30
Manjunath 0b6cdc8e1f Merge branch 'main' into feat/voice-geometry
Picks up the terrain partial-heights work (#1456). No conflicts; it changes
no voice prompt or tool description.
2026-11-03 17:58:29 +05:30
German Khokhlov 490c8d1769 Add the changelog entry for #1521 2026-11-03 16:27:25 +04:00
German Khokhlov 415f23739f Name the reporting provider in Contacts and Cockpit counts
The Cockpit Contact counts named fixed providers (OpenSky, adsb.lol,
AISStream) while the Layers panel and Contacts named the provider the last
snapshot reported, and the vessels layer kept that name out of getStats(),
so Contacts always said AISStream. Each cohort now carries the provider and
whether it answered as a fallback or from a stale snapshot, and both views
show it the way the Layers row does ("adsb.lol · FALLBACK").

Fixes HAL-721.
2026-11-03 16:26:55 +04:00
Manjunath 03e9db07c1 fix(voice): skip the outline lookup for a sized area around a landmark
A radius the user gives always draws a buffer, but the enclosing outline
was still looked up first (up to 6 s and a guarded Nominatim request) and
then thrown away. It is no longer requested. The slow-outline test now
checks the default 250 m fallback it was meant to cover.
2026-11-03 17:55:44 +05:30
Manjunath 670e952c29 fix(voice): name the outline source that answered
The outline rungs recorded only the credit kind, so the voice area code
never saw 'openfreemap' or 'nominatim' and stored every such outline as
the Overpass rung, even with no Overpass configured. Voice geometry tagged
the source originally; the merge with main kept main's untagged rungs and
its test pinning the absence. The tag is restored and the test now asserts
it for both sources.
2026-11-03 17:55:38 +05:30
Manjunath 552f3de3ff fix(voice): resolve area names to the place they name
Three resolver fixes from checking the review focus:
- A natural region named like a country or state no longer hides it.
  "India" was the peninsula, "Japan" an island group, "Florida" the
  peninsula and "Punjab" a desert, so bare "Punjab" never asked which one.
  The region still answers when asked for by level, and when no unit truly
  has the name ("Great Britain" finds the United Kingdom only by alias).
- Same-named units get their own candidate: "Washington" offered WA and DC
  with one id, so either choice was DC and a DC lookup overwrote a stored
  Washington State. Ids now carry the region code, and choices show it.
- A bundled unit that only shares the name ("Azad Kashmir" for "Kashmir")
  no longer ends the search; the outline ladder still looks, and the
  mismatch is kept if it finds nothing. Retired zones stay refused.
2026-11-03 17:55:33 +05:30
German Khokhlov f3adccd01a Merge pull request #1456 from quanticsoul4772/upstream/terrain-partial-heights
fix(terrain): keep known heights when a terrain request fails
2026-11-03 16:03:07 +04:00
Manjunath d18fdfa47f Merge branch 'main' into feat/voice-geometry
Picks up the HUD summary locale work (#1488). No conflicts; it changes no
voice prompt or tool description.
2026-11-03 17:31:36 +05:30
Manjunath dfdbb134d5 fix(osm): key cached place searches to the Overpass instance that answered
The /api/osm/features cache key was the query alone, kept for a day, so after
an operator repointed OVERPASS_UPSTREAMS the old instance's answers were still
served. The key now includes the configured-upstream tag, as main's other
Overpass caches check it, so a new instance is always asked.
2026-11-03 17:27:36 +05:30
Manjunath 474cce51eb fix(voice): keep the area in follow-ups on OSM places
osm_query handed its places to the analyst with no presentation, so "which of
those have emergency?" after "hospitals in Kathmandu" was answered as "anywhere
in the loaded data". The handoff now carries the scope the places were found
in ("inside Kathmandu" with its areaId, or "in view"), as an analyst count over
that area would.
2026-11-03 17:27:32 +05:30
Manjunath 451be8e3b7 fix(voice): treat a Zone as retired only in Nepal
Every name ending in "Zone" was kept without a level and refused as a retired
administrative identity. That is right for Nepal, which replaced its zones in
2015, but zones are current units elsewhere: Ethiopia's "Gamo Zone" was
refused. The selector now keeps the verified level unless display_name places
the zone in Nepal or names no country, and the client no longer reads "Zone"
as a retired level when the source verified one.
2026-11-03 17:27:26 +05:30
Manjunath 682427ef1d docs(changelog): move the voice geometry entries to a changelog.d fragment
The maintainer workflow asks runtime changes for a changelog.d/<pr>-<slug>.md
fragment and no edit to CHANGELOG.md. The three entries move unchanged, with
their credit, into changelog.d/846-voice-geometry.md.
2026-11-03 17:27:20 +05:30
Manjunath 4edaf245d0 fix(voice): keep say lines and retry advice in cancelled and failed replies
The cancelled and failed branches ran before the say-line branch, so a
cancelled panel change no longer said "Cockpit exited; the voice panel was
left as you last set it", and AREA_TIMEOUT / OSM_TIMEOUT got "tell the user it
failed" instead of the tool description's "retry once". A cancelled or failed
result with a say line now has it spoken, and those two timeouts are retried
once. Both providers share this instruction.
2026-11-03 17:27:14 +05:30
German Khokhlov 4e950d04e9 Merge remote-tracking branch 'origin/main' into claude/hal-676-gev-fire-intensity-742bd1
# Conflicts:
#	scripts/format-scope.json
#	src/i18n/locales/ar.json
#	src/i18n/locales/de.json
#	src/i18n/locales/fr.json
#	src/i18n/locales/it.json
#	src/i18n/locales/ja.json
#	src/i18n/locales/pt.json
#	src/i18n/locales/zh-Hant.json
#	src/ui/layerPanel.js
2026-11-03 15:36:13 +04:00
German Khokhlov c664f8eacb Merge branch 'main' into upstream/terrain-partial-heights 2026-11-03 15:28:20 +04:00
German Khokhlov 39c648094b Merge pull request #1488 from bilawalsidhu/german/hal-623-hud-summary-locale
fix(hud): AI summary in the UI language, canonical feed state, partial aggregation
2026-11-03 15:22:40 +04:00
German Khokhlov 342d960582 fix(traffic): retain line identity across clipping 2026-11-03 15:14:20 +04:00
Manjunath 2ac73519ec Add changelog fragment for voice camera tilt (#1520) 2026-11-03 16:43:01 +05:30
Manjunath 75000cf6a9 Hold a voice orbit, pan or rotate until its fly-to lands
"Fly to Paris and orbit it" reaches the app as two sibling tool calls,
which OpenAI runs at once. With a place that needs a lookup, the orbit
started over the old view and the late flight then replaced it; with a
known place, pan and rotate cancelled the flight already in the air.

Orbit, pan and rotate now wait for a pending voice flight the same way
tilt does: up to 30 s, and not run (saying so) if the flight is
cancelled, finds nothing or times out. Stop and rotate_globe do not
wait. Orbit keeps its own arming for flights the voice runner does not
track.
2026-11-03 16:42:50 +05:30
Manjunath 91a3f787d4 Hold a voice tilt until its fly-to lands
"Fly to London and tilt the camera" reaches the app as two sibling tool
calls, and OpenAI runs them concurrently. The tilt's navigation step
cancelled the flight already in the air, so the camera tilted where it
was; when the place still needed a lookup, the late flight replaced the
tilt and London came out top-down.

The voice runner now tracks its latest flight. A tilt sent while that
flight is still looking up or flying waits for it to land (up to 30 s),
then tilts at the destination. If the flight is cancelled, finds nothing
or times out, the tilt is not run and says so.
2026-11-03 16:42:39 +05:30
Manjunath 69152868e4 Fix voice camera tilt from a top-down view
A voice fly-to frames a city top-down, at pitch -90. The tilt clamp
rejected any step that left the pitch below -89, so the first frame of
"tilt up" stopped the motion and "tilt down" was refused: from the view
voice lands on, no tilt ever moved the camera. A bare "tilt the camera"
also failed because tilt required a direction.

- Clamp tilt only in the direction of travel, so -90 can tilt up.
- Swing the camera about the ground under the crosshair, so the place
  stays centred and the heading is kept. A tracked view still tilts in
  place, and a pivot tilt yields when tracking takes the camera.
- Add an optional move_camera pitchDeg for "tilt 45 degrees"; a tilt
  with neither angle nor direction settles at -45 (oblique view).
- Re-derive the voice tool schema pins for the new argument and wording,
  and keep the Realtime prompt under its 52,000-character budget.
2026-11-03 16:42:18 +05:30
Manjunath 05edae0606 Merge branch 'main' into feat/voice-geometry
Picks up the Radio Next/Previous fix (#1474) and the Codespaces docs
(#1515). The one conflict is the Radio cycle assertion in
gevActions.test.mjs: main's new fallback flag is kept in this branch's
layout.
2026-11-03 16:40:39 +05:30
German Khokhlov 92666fdbaf fix(cyclones): preserve movement in selected voice context 2026-11-03 15:10:20 +04:00
German Khokhlov 65f0467ad8 fix(infrastructure): sample visible ground below the ellipsoid 2026-11-03 15:10:15 +04:00
German Khokhlov fae50c0791 fix(cctv): display colour-neutral Bosch MIC models 2026-11-03 15:08:36 +04:00
German Khokhlov c68f167acb fix(transit): count distinct coverage regions 2026-11-03 15:07:00 +04:00
German Khokhlov 04d626a4b1 fix(datacenters): preserve missing geometry and compare ring topology 2026-11-03 15:02:42 +04:00
Manjunath 20770e52a9 docs(changelog): add the range ring sonar pulses fragment for #1519 2026-11-03 16:31:02 +05:30
Manjunath e80f583e66 feat(awareness): add calm sonar pulses that flash the range ring and echo contacts
Pulses: three dim rings grow from the Global Context subject to the
250 km range ring at constant speed, one every 3 s, each taking 9 s.
Each fades in over its first 8% of travel, then fades as
(1 - travel)^1.5 to about a third of its 60% peak, so it arrives at the
ring dim but visible. They are 3 px in the ring's colours, as polylines
with CallbackProperty positions and colours that share the ring's
centre object, so they follow a moving subject.

Ring: while pulses run, the 250 km ring rests unseen and flashes to full
strength as each pulse reaches it, fading over 1.8 s. Without pulses
(reduced motion, or a scene with no primitives or preRender hook) it
stays on screen as before.

Gradient: behind each pulse a radial band in the same blue fades inward
over 35% of the radius. One flat, translucent, unpickable 250 km disc
draws it with a small Fabric material. A preRender listener writes the
pulse radii and opacities into two vec4 uniforms each frame. The disc
(0.25 degree granularity, ~3.5 ms to build) is rebuilt only after the
subject moves 2 km.

Echoes: as a pulse front crosses a flight, military aircraft or AIS
vessel, the detection overlay draws a second bracket in the pulse blue
around that contact's own bracket, spreading outward as it fades over
1.4 s. Awareness publishes the field (centre, radius, pulse timing,
echo styling) through the new src/awarenessEcho.js. The overlay reads
it, so no layer's icon or colour is touched. Mapped installations have
no detection bracket, so they do not echo.

Rendering cost: pulses never hold continuous rendering. They ask for a
frame every 80 ms, the Cyber sonar's parked cadence, so a parked
Contacts view repaints at ~12.5 fps while a subject is shown. This
changes the "parked Contacts stays quiet" behaviour on purpose.
clearVisual stops the timer and removes the disc, the listener and the
echo field.

awarenessRangeRingPositions can now reuse a result array, and passes the
WGS84 ellipsoid explicitly so a reused Cartesian never lands in the
ellipsoid argument. Tuning lives in AWARENESS_RANGE_PULSE,
AWARENESS_RANGE_ECHO and AWARENESS_RANGE_RING_STYLE. src/awarenessEcho.js
is registered in the awareness-layer, application-components and
application-layer-construction packages.

Tests cover:
- the pulse curve and calm rate;
- the ring flash once per arrival, and the static ring without pulses;
- the 80 ms frame cadence and its stop;
- the partial-scene fallback;
- the gradient disc: unpickable, uniform values, rebuild and cleanup;
- per-frame evaluation, and the echo field following the subject;
- awarenessEcho strength, rim wrap, layer filter and altitude handling;
- the detection renderer's echo-bracket band and outward spread.
2026-11-03 16:30:56 +05:30
Manjunath 8a50705eea docs(changelog): add the range ring visibility fragment for #1518 2026-11-03 16:30:37 +05:30
Manjunath 2a629f0987 fix(awareness): make the 250 km range ring readable on any ground
The Global Context range ring was a Cesium ellipse outline at 1 px in
rgba(98,181,255,0.72). WebGL ignores larger ellipse outline widths, so it
could not be thickened, and it vanished over desert and snow and under the
FLIR, NVG and CRT styles.

Draw it as a polyline instead: a 4 px line with a 2 px Context-blue core
inside a 1 px dark halo on each side (PolylineOutlineMaterialProperty).
The halo gives luminance contrast on light and dark ground, which the
monochrome styles keep. The ring sits at the same 2.5 km height and
follows the subject on the same 250 m threshold. Positions come from a
new awarenessRangeRingPositions helper that stays within 50 m of 250 km.

Style values live in AWARENESS_RANGE_RING_STYLE (awareness/policy.js).
Adds rangeRing.test.mjs (geometry at equator, Sahara, Greenland and the
antimeridian; contrast; entity shape; follow threshold) and a
CURRENT-STATE note.
2026-11-03 16:30:20 +05:30
German Khokhlov be53c338c8 Merge branch 'main' into upstream/terrain-partial-heights 2026-11-03 14:59:12 +04:00
German Khokhlov 3139369673 Add a heatmap spot size slider to the Active Fires row
A ½×–4× slider (log2 axis, 5 % steps, default 1×) under the intensity
filter sets how large far-zoom heat spots are. It scales the grid, not
the blur: 2× is a 1024 × 512 grid with the same one-pixel blur, so each
spot grows exactly and weak lone fires don't fade as they widen. A change
rebuilds only the heatmap. Share links keep it as w.s (percent, 50–400).
The layer row now takes a list of sliders.
2026-11-03 14:56:40 +04:00
German Khokhlov d413362467 Merge branch 'main' into german/hal-623-hud-summary-locale 2026-11-03 14:55:40 +04:00
German Khokhlov b80c54b7f1 Merge pull request #1474 from bilawalsidhu/fix/hal-722-radio-next-no-autoplay
Keep Radio Next and Previous from starting playback before Play (HAL-722)
2026-11-03 14:55:14 +04:00
German Khokhlov ac5164b14b Merge remote-tracking branch 'origin/main' into german/hal-623-hud-summary-locale 2026-11-03 14:45:45 +04:00
German Khokhlov 03c168d7c9 refactor(hud): share the warning line and trim the summary helpers
One provenanceLine() builds STATE | text | LAYERS for the HUD and
get_hud_caption. hasWord is one lookaround regex, capAtWord one grapheme
pass. Feed-state helpers are imported from layerSnapshot.js directly, and the
kept-whole sanitizer cases are one table.
2026-11-03 14:45:32 +04:00