Commit Graph
2036 Commits
Author SHA1 Message Date
Sameh Khamis 6ce9498d18 fix(voice): move focus with the voice panel that chat asks for
Asking from the chat composer to open Voice Settings or Voice Preview
closed chat around the focused input, leaving focus on a hidden control.
Focus held in chat now moves to the requested view's tab, as the panel's
own tabs move it; focus anywhere else stays put.
2026-11-03 16:04:54 -08:00
Sameh Khamis 66fb4b6aca test(voice): pin the deadline each chat map action gets
Three 40 ms actions in one round outlast a 50 ms deadline unless each
action re-arms it, so removing that re-arm now fails the test for both
providers.
2026-11-03 15:54:59 -08:00
Sameh Khamis eed6162753 fix(voice): follow a new chat request once the log is full
The conversation is kept to its newest 60 messages, so once full it no
longer grows as a message is sent, and a reader who had scrolled up was
left behind by their own request. The view now spots a send by the newest
user message's id instead of the list's length.
2026-11-03 15:54:54 -08:00
Sameh Khamis 51b561815c fix(voice): say the attachment limit is 10 KB in total
The attach menu promised up to 10 KB per file, but the attachments share
one 10 KB budget, so two 6 KB files were refused. The hint now says the
limit covers them all, in every locale.
2026-11-03 15:54:54 -08:00
Sameh Khamis 829db19f60 fix(voice): keep the voice preferences in reach from chat Settings
With Voice Preview off the voice panel never opens on its own, and chat
Settings hid the voice preferences, so the Preview toggle, inactivity
timeout and model tier could not be reached by pointer. Chat Settings now
show them below the chat options, still in chat's frame and without the
voice tabs.
2026-11-03 15:54:54 -08:00
Sameh Khamis 4f4af9c4e0 fix(voice): show the voice panel that typed chat asks for
set_panel_open for voice-settings or voice-preview reported success while
chat's open state kept the voice panel hidden, minimized chat included. A
successful explicit show now closes chat, which keeps the typed session,
its running turn and its messages; reopening chat restores them.
2026-11-03 15:54:54 -08:00
Sameh Khamis b6967a3dd9 Merge branch 'main' into feat/typed-ai-chat
# Conflicts:
#	src/i18n/locales/ar.json
2026-11-03 15:45:43 -08:00
Sameh Khamis c66f8f039b test(cctv): make the oversized Houston catalog fixture valid JSON
The streamed oversized body was invalid JSON, so the loader returned
nothing whether or not the byte cap applied. Pad a valid catalog with a
usable camera instead, so only the cap can refuse it.
2026-11-03 15:04:05 -08:00
Sameh Khamis f120989a2b docs(cctv): say the Houston pack needs TxDOT's written permission to enable
TranStar's Content Usage terms prohibit copying, distribution and
external framing of its images without TxDOT's prior written
permission. The opt-in text only said the pack was off unless
CCTV_HOUSTON_ENABLED=1, without saying what an operator needs before
setting it. Say "enable only if you hold TxDOT's written permission" in
.env.example, the README, DATA_SOURCES.md, CURRENT-STATE and the
changelog, matching the NYC and Florida packs, and fix the column of the
.env.example comment.

The houston-cctv credit now reads "(Houston, when enabled)" and links
the Content Usage terms, like the Florida credit. Where the docs said
frames are "never stored or redistributed", they now say what happens:
frames are relayed on demand, buffered only per request, sent with
no-store and not archived.
2026-11-03 15:03:37 -08:00
Sameh Khamis 8cd0cc82f5 fix(cctv): read TranStar dir as the camera's side of the road, at low confidence
TranStar's `dir` field is the side of the freeway a camera sits on, not
the way it faces. TranStar's own map script (translateCameraDirection)
shows it as "Traffic closest to the camera is traveling": South is
eastbound, North westbound, West southbound and anything else
northbound. The live catalog agrees: I-10 cameras are North/South and
I-45 cameras East/West.

Treating `dir` as a high-confidence facing pointed most Houston cameras
across or away from the road they watch, and dropped the "estimated"
marking. Map it to the travel direction instead (S 90, N 270, W 180,
E 0) as a low-confidence prior with the low-confidence pose, and keep
the id-hash fallback for None or unknown values.
2026-11-03 15:02:30 -08:00
Sameh Khamis 09bdc314d0 Merge branch 'main' into fix/houston-cctv-pack 2026-11-03 15:01:13 -08:00
Sameh Khamis 329ef7e01c test(drone-airspace): check the share token in CURRENT-STATE, not the changelog fragment
npm run changelog consumes changelog.d fragments at release, so a test that reads the fragment would fail with ENOENT afterwards. CURRENT-STATE is permanent.
2026-11-03 14:45:05 -08:00
Sameh Khamis 266825eddb fix(drone-airspace): show FAA times-of-use line breaks as separators
FAA marks line breaks in TIMESOFUSE with a literal %, which 56 live
restricted-area cards showed verbatim ("SAT - SUN; %OTHER TIMES BY NOTAM").
Read each marker as a '; ' separator, absorbing a semicolon already before
it.
2026-11-03 14:36:57 -08:00
Sameh Khamis add905e6b9 docs(drone-airspace): correct the share token, voice support and coverage
The changelog fragment named share-link token 6 (sea marks); the layer
reserves 8. CURRENT-STATE said voice could toggle and query the layer and
listed it among the analyst record sources, but it is in VOICE_OFF_LAYERS.
Say voice is off for now, and that the layer covers FAA prohibited and
restricted special-use airspace only, not the UAS Facility Map / LAANC grids
or TFRs. A test pins these claims to the token ledger and the voice manifest.
2026-11-03 14:35:59 -08:00
Sameh Khamis 585392cbcf fix(drone-airspace): refuse cross-site requests to the FAA proxy
A foreign Origin or Sec-Fetch-Site: cross-site request was answered and
counted against the client's 60-a-minute quota, and could start a refresh.
Run the keyless same-site checks the other map routes use before rate
limiting and cache acquisition; reverse-proxy forwarding headers stay
accepted. List the route with the keyless map routes in SECURITY.md.
2026-11-03 14:35:14 -08:00
Sameh Khamis ca17412c70 fix(drone-airspace): redraw a zone when only its boundary changes
The refresh signature left out the polygons, and these rows carry no
timestamp that moves with the geometry, so an amended boundary with the same
attributes kept the old shape and card anchor until the layer was destroyed.
Add a deterministic hash of each zone's rings to the signature.
2026-11-03 14:34:28 -08:00
Sameh Khamis 8e63420f06 fix(drone-airspace): show stale FAA snapshots as stale and cap them at a day
The source dropped the proxy's stale flag and fetch time, so the layer row
read nominal and freshly updated through an outage, and the proxy served its
last snapshot indefinitely. Carry stale and fetchedAt through the source as
Fire Perimeters does, date a stale snapshot by its fetch, and stop serving a
snapshot more than 24 hours old.
2026-11-03 14:34:01 -08:00
Sameh Khamis 6bdedd8523 fix(drone-airspace): refuse empty or incompletely paged FAA snapshots
An upstream answer whose zones all fail validation normalized to an empty
list, and paging that stopped at the page limit while the service still
flagged more records was accepted as complete. Both were cached as a fresh
snapshot for an hour and replaced the last good one. Treat them as failed
refreshes so stale-on-error keeps serving the previous snapshot.
2026-11-03 14:33:10 -08:00
Matt Van Horn 08b8be26fe Merge remote-tracking branch 'upstream/main' into fix/drone-airspace
# Conflicts:
#	docs/CURRENT-STATE.md
#	src/app/constructCatalog.test.mjs
#	src/app/sourceComposition.test.mjs
#	src/data/layerState.test.mjs
#	src/data/layerStateTokenLedger.test.mjs
#	src/i18n/locales/ar.json
2026-11-03 11:55:42 -08:00
manjunath22466 340ba840ac Merge branch 'main' into feat/typed-ai-chat 2026-11-04 00:34:22 +05:30
Sameh Khamis 0e3b1f67cf Merge branch 'main' into manjunath/hal-769-gev-ux-improve-visibility-of-the-250-km-radius-circle
# Conflicts:
#	scripts/format-scope.json
#	src/layers/awareness/policy.js
#	src/layers/awareness/rendering.js
2026-11-03 11:00:53 -08:00
manjunath22466 976ea38a7f Merge branch 'main' into feat/typed-ai-chat 2026-11-04 00:30:37 +05:30
Sameh Khamis 4fc889ad19 Merge branch 'main' into manjunath/hal-769-range-ring-visibility 2026-11-03 10:48:55 -08:00
manjunath22466 39165bb199 fix(voice): offer typed chat the same Overpass tools as voice
Main now offers osm_query, and names it in the prompt, only when Overpass
is configured. Typed chat follows the same switch on both providers: its
prompt, the tools it offers and the calls it accepts.
2026-11-04 00:17:54 +05:30
manjunath22466 fe78b3ebc9 Merge branch 'main' into feat/typed-ai-chat 2026-11-04 00:17:47 +05:30
Sameh Khamis 02a6739466 fix(awareness): follow a gesture to its last contact, and drop it on blur
Lifting one finger of a pinch removed the document listeners, so the
remaining finger's drag, which Cesium carries on with, stopped counting.
A gesture now ends only when its last pointer lifts or no touches are
left. And a gesture whose end never reaches the page (the window loses
focus mid-drag, or the page is hidden) left the listeners attached, so
a later unrelated drag counted as camera input; a blur or hidden page
now stops following it and drops its pending input.
2026-11-03 10:47:23 -08:00
Matt Van Horn 038b4f3ef6 Merge remote-tracking branch 'upstream/main' into fix/houston-cctv-pack
# Conflicts:
#	docs/CURRENT-STATE.md
2026-11-03 10:41:50 -08:00
Sameh Khamis de634952ec fix(awareness): keep a long drag or touch counting as camera input
Only canvas events refreshed the 1.5 s input window, and a touch only on
touchstart. Cesium follows a drag that leaves the canvas at document
level (in Firefox, and wherever there are no pointer events), so a long
gesture kept moving the camera after the window closed, and the pulses
rested mid-gesture. From a pointer or touch start on the canvas until
its end or cancel, moves at document level now count too; the listeners
go when the gesture ends or the layer detaches.
2026-11-03 10:40:30 -08:00
Sameh Khamis 56a7cacbec fix(awareness): ignore canvas input while the cockpit owns the camera
The cockpit turns Cesium's camera controls off and moves the camera on
its own, so a click, drag or wheel on the canvas moved nothing yet still
counted as the user handling the map: the cockpit's next move woke a
nine-second pulse burst. Input now counts only while the camera controls
take it, and a gesture still pending when another owner takes the
camera is dropped.
2026-11-03 10:39:09 -08:00
Sameh Khamis fe3d17df02 fix(awareness): give the pulse material a named type so Cesium caches it once
Cesium keeps every material type in a global cache for the page's
lifetime. The pulse disc's fabric had no type, so each new animated
visual (a subject switch, a reselect, reduced motion turned off) added a
GUID-named entry that was never removed. Name the type, and give each
material built from the cached template its own uniform objects back.
2026-11-03 10:39:06 -08:00
German Khokhlov e8735163c7 Merge branch 'main' into upstream/traffic-paired-flow-density 2026-11-03 22:38:31 +04:00
Sameh Khamis daba7f93a7 docs(awareness): state the range ring's measured ~50 m accuracy
The ring is up to about 49 m off the geodesic radius at 45 degrees, not
a few metres. Also attach the range ring style's JSDoc to its constant.
2026-11-03 10:34:19 -08:00
Sameh Khamis 39a697aa3c fix(awareness): keep the range ring a full circle at the poles
At or within about 1e-7 degrees of a pole the latitude/longitude
destination formula cancels out, and the 128-point ring collapsed to
three distinct points joined by chords of about 500 km. Step along the
great circle in 3D from the centre's up, north and east unit vectors
instead. Results at ordinary latitudes and across the antimeridian are
unchanged.
2026-11-03 10:34:10 -08:00
Sameh Khamis 0d32f59b40 Merge branch 'main' into manjunath/hal-769-range-ring-visibility
# Conflicts:
#	scripts/format-scope.json
2026-11-03 10:33:19 -08:00
Sameh Khamis 553f0c06ee Merge branch 'main' into feat/voice-geometry 2026-11-03 09:49:42 -08:00
manjunath22466 7618be4930 Merge branch 'main' into feat/typed-ai-chat 2026-11-03 23:17:25 +05:30
Sameh Khamis 4f95763a28 style: format the new area and place-search tests 2026-11-03 09:32:03 -08:00
Sameh Khamis f675938412 test(voice): record the measured prompt size, 55,210
The budget test measures the full instructions plus every tool. At this
head that is 55,210, ~290 under the 55,500 ceiling; the note said 55,370,
which that measurement does not produce.
2026-11-03 09:30:14 -08:00
Sameh Khamis 6b92602589 fix(osm): key place-search clients by IPv6 /64
/api/osm/features keyed its rate limit and its one-search-per-client slot
by the full peer address, so an IPv6 host could rotate addresses inside
its /64 to take both global slots and slip the 12/min limit. It now uses
clientNetworkKey, as /api/overpass does.
2026-11-03 09:29:52 -08:00
Sameh Khamis b531dd545b Merge branch 'feat/voice-geometry' into maintainer fixes
Picks up acd87e6c: the keyless cross-site gate on /api/osm/features
(582bfce6) and main at 93804d5d. SECURITY.md, the format-scope list and
the schema-test comments take the branch's side; the place-search budget
wording from the timeout fix is kept.
2026-11-03 09:29:23 -08:00
Sameh Khamis cc3d61a960 fix(camera): keep the zoom ceiling fixed while tracking a target
While the camera tracks an entity or looks at a point, Cesium measures
zoom distance from the target, not the Earth. Raising the ceiling to the
camera's altitude then lifted it with every outward step, so a tracked
camera could zoom out without limit. The guard now follows the altitude
only for an untracked 3D camera and otherwise keeps its 40,000 km
ceiling.
2026-11-03 09:29:22 -08:00
manjunath22466 c09de47d19 fix(voice): mark a stopped reply's running action as cancelled
The action under way when a reply stops is answered as cancelled, since it
may have taken effect, and only later calls as skipped; a stop during a
round's first action keeps that round too. The closing note no longer
claims the actions succeeded. Tests cover the refusal body read cap and
the release of an unread refusal body.
2026-11-03 22:57:22 +05:30
Manjunath a851199f5b fix(awareness): rest the range pulses unless the user moves the camera
Review follow-ups for the sonar pulses.

Wake on user input only: a camera move wakes the pulses only within
1.5 s of a pointer, wheel or touch on the Cesium canvas, where its
camera controller listens. Tracking, the cockpit view, an orbit or a
fly-to move the camera on every frame and no longer keep the pulses
running. Keys don't count: the camera controller reads none.

Below the disc: with the camera under 2.5 km the pulses rest
completely, with no disc, ring flash, echoes or frame requests.

Echo brightness: echoes scale by the contact bracket's drawn alpha,
which includes the Cyber sonar sweep, rounded to the nearest band with
no floor, so an echo is never brighter than its bracket.

Disc follow: the pulse disc follows the subject by a model-matrix
rotation about the Earth's centre and is rebuilt only every 20 km
instead of every 500 m.

Tests: a cap on burst length; teardown through disable(); the echo's
spread and alpha on the canvas; echoes under the Cyber sonar never
brighter than their bracket; waking by pointer, wheel, touch and drag
but not by keys, app camera moves or stale input; the rest below the
disc; and tighter asserts. CURRENT-STATE notes that terrain above
2.5 km still cuts through the disc and ring.
2026-11-03 22:50:32 +05:30
Sameh Khamis 16b383ab6f fix(area): detach a lookup's abort listener once it settles
Each resolve() that raced the caller's signal added an abort listener and
never removed it, so a long-lived signal collected one listener per
completed lookup. The listener is now removed when the call settles and
is not added at all to a signal that is already aborted.
2026-11-03 09:16:52 -08:00
Sameh Khamis 3d2896cf27 fix(osm): default an absent /api/osm/features limit to 500
Without a limit parameter the route read null, Number(null) is 0, and the
clamp turned that into a one-feature list. An absent or empty limit now
gets the documented default of 500.
2026-11-03 09:13:40 -08:00
Sameh Khamis 68667e2e3c fix(area): accept "State of Texas" and "USA" as the units they name
resolve_area compared the spoken name with the unit's name literally, so
"State of Texas" (admin1) and "USA" or "UK" (country) were refused with
AREA_IDENTITY_MISMATCH although the bundled matcher found exactly those
units. Both identity checks, the bundled one and the outline ladder's, now
also accept the prefix forms and same-unit country aliases the matcher
reads, at the level they imply, so the level check still applies.
"Great Britain", "Britain", "America" and "Korea" still need their own
names, and the natural-region check stays literal.
2026-11-03 09:12:28 -08:00
Sameh Khamis 9e3786716d test(camera): pin that application controls install the zoom-out guard
The guard's own tests pass even if createApplicationControls stops
installing it. This checks the ceiling and frame listeners are in place
after construction and gone after teardown.
2026-11-03 09:11:51 -08:00
Sameh Khamis b817d5802b fix(camera): let a two-finger tilt win over finger-spacing drift
Above 300 km the guard treated any shrinking pinch distance as an outward
zoom, so a two-finger tilt whose fingers drifted slightly closer had its
pitch overwritten. A two-finger gesture now counts as a zoom only when the
spacing changes more than the fingers' midpoint moves.
2026-11-03 09:10:58 -08:00
Sameh Khamis e4b3933166 fix(camera): read zoom input at the ceiling when the scene is idle
The guard reads its input in preRender, which Cesium only fires on frames
that draw. In request-render mode, wheel input at the ceiling doesn't move
the camera, so nothing drew and the input was applied later by an
unrelated frame. The guard now requests a frame through the render
governor whenever it has unread input.
2026-11-03 09:10:30 -08:00
Sameh Khamis 08ff7f3e9d fix(camera): let a camera above the zoom ceiling zoom in without a jump
Cesium clamps any zoom that starts above maximumZoomDistance to that
limit, in either direction, so a share link or flight that left the
camera above 40,000 km jumped to the ceiling on the first wheel tick.
While the camera is above the ceiling, the guard now uses its current
height as the limit and lowers it as the camera descends.
2026-11-03 09:09:55 -08:00