mirror of
https://github.com/bilawalsidhu/gods-eye-view.git
synced 2026-11-04 18:34:02 +00:00
fix(agent): align World Packet with reviewer feedback and voice parity
Reuse one context/contacts/tracking owner in voice and World Packet. Compare voice outputs after stripping presentation fields; remove the unconsumed fingerprint API pending an actual caller. Document the exported packet boundary and reject null options safely. Review guidance: asturksever on origin #880; Windows parity diagnosis by quanticsoul4772. Existing source ownership retained.
This commit is contained in:
1 parent
83fc895442
commit
1c4c903ac6
5 files changed
+34
-181
No files matched your search
@@ -6,7 +6,7 @@ import {
|
||||
} from '../data/layerSnapshot.js';
|
||||
import { contextModeWord } from '../contextModePolicy.js';
|
||||
|
||||
const TRACKABLE_FAMILIES = Object.freeze([
|
||||
export const TRACKABLE_FAMILIES = Object.freeze([
|
||||
{ layerId: 'flights', kind: 'aircraft' },
|
||||
{ layerId: 'military', kind: 'aircraft' },
|
||||
{ layerId: 'ais-live-vessels', kind: 'vessel' },
|
||||
@@ -24,37 +24,6 @@ const NESTED_CONTEXT_RESULT_FIELDS = Object.freeze([
|
||||
'contextRollback',
|
||||
]);
|
||||
|
||||
const NON_SEMANTIC_PACKET_KEYS = new Set(['note', 'ageLabel', 'generatedAt']);
|
||||
|
||||
function semanticPacketValue(value) {
|
||||
if (Array.isArray(value)) return value.map(semanticPacketValue);
|
||||
if (!value || typeof value !== 'object') return value;
|
||||
const out = {};
|
||||
for (const key of Object.keys(value).sort()) {
|
||||
if (NON_SEMANTIC_PACKET_KEYS.has(key)) continue;
|
||||
out[key] = semanticPacketValue(value[key]);
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
/**
|
||||
* Stable semantic fingerprint for change detection, not authentication.
|
||||
*
|
||||
* Presentation-only narration/age labels are excluded. Feed state, source,
|
||||
* lastUpdate, camera, context, tracked entities, and other semantic state stay
|
||||
* inside the digest.
|
||||
*/
|
||||
export function semanticWorldPacketFingerprint(packet) {
|
||||
const canonical = JSON.stringify(semanticPacketValue(packet));
|
||||
let hash = 0xcbf29ce484222325n;
|
||||
const prime = 0x100000001b3n;
|
||||
for (let i = 0; i < canonical.length; i += 1) {
|
||||
hash ^= BigInt(canonical.charCodeAt(i));
|
||||
hash = BigInt.asUintN(64, hash * prime);
|
||||
}
|
||||
return `fnv1a64:${hash.toString(16).padStart(16, '0')}`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Translate context-mode payloads into the public tool vocabulary while
|
||||
* retaining the internal id beside each translated field.
|
||||
@@ -124,13 +93,14 @@ export function activeContactsWindow(dataManager) {
|
||||
* This performs no network requests and intentionally mirrors the historical
|
||||
* get_current_view_state payload so voice can delegate without behavior drift.
|
||||
*/
|
||||
export function getWorldPacket({
|
||||
viewer,
|
||||
styleManager,
|
||||
dataManager,
|
||||
sceneDirector = null,
|
||||
now = Date.now(),
|
||||
} = {}) {
|
||||
export function getWorldPacket(options = {}) {
|
||||
const {
|
||||
viewer,
|
||||
styleManager,
|
||||
dataManager,
|
||||
sceneDirector = null,
|
||||
now = Date.now(),
|
||||
} = options ?? {};
|
||||
if (!viewer?.camera?.positionWC)
|
||||
throw new TypeError('World Packet requires a viewer camera');
|
||||
if (!dataManager || typeof dataManager.getAll !== 'function')
|
||||
|
||||
@@ -2,12 +2,17 @@ import test from 'node:test';
|
||||
import assert from 'node:assert/strict';
|
||||
import * as Cesium from 'cesium';
|
||||
|
||||
import {
|
||||
getWorldPacket,
|
||||
semanticWorldPacketFingerprint,
|
||||
} from './worldPacket.js';
|
||||
import { getWorldPacket } from './worldPacket.js';
|
||||
import { createGevActionRunner } from '../voice/gevActions.js';
|
||||
|
||||
function withoutVoicePresentation(result) {
|
||||
const packet = { ...result };
|
||||
delete packet.say;
|
||||
delete packet.display;
|
||||
delete packet.schedule;
|
||||
return packet;
|
||||
}
|
||||
|
||||
function harness() {
|
||||
const position = Cesium.Cartesian3.fromDegrees(-97.74, 30.26, 500);
|
||||
const viewer = {
|
||||
@@ -64,7 +69,7 @@ test('World Packet preserves get_current_view_state parity', async () => {
|
||||
const runner = createGevActionRunner({ viewer, dataManager, styleManager });
|
||||
const voice = await runner('get_current_view_state');
|
||||
|
||||
assert.deepEqual(voice, packet);
|
||||
assert.deepEqual(withoutVoicePresentation(voice), packet);
|
||||
assert.equal(packet.context.mode, 'contacts');
|
||||
assert.equal(packet.context.modeInternal, 'flights');
|
||||
assert.equal(packet.layers[0].feedState, 'nominal');
|
||||
@@ -94,20 +99,6 @@ test('World Packet keeps stale/unavailable feed semantics distinct from empty',
|
||||
assert.match(packet.feedProvenance.note, /fixture unavailable/i);
|
||||
});
|
||||
|
||||
test('semantic fingerprint ignores narration prose but changes with observed state', () => {
|
||||
const { viewer, dataManager, styleManager } = harness();
|
||||
const packet = getWorldPacket({ viewer, dataManager, styleManager });
|
||||
const original = semanticWorldPacketFingerprint(packet);
|
||||
|
||||
const proseOnly = structuredClone(packet);
|
||||
proseOnly.feedProvenance.note = 'different generated narration';
|
||||
assert.equal(semanticWorldPacketFingerprint(proseOnly), original);
|
||||
|
||||
const changed = structuredClone(packet);
|
||||
changed.layers[0].count += 1;
|
||||
assert.notEqual(semanticWorldPacketFingerprint(changed), original);
|
||||
});
|
||||
|
||||
// The 2026-10 main voice contract omits disabled layers from the model payload.
|
||||
test('World Packet and voice preserve enabled-only payload and count disabled layers', async () => {
|
||||
const { viewer, dataManager, styleManager } = harness();
|
||||
@@ -134,22 +125,9 @@ test('World Packet and voice preserve enabled-only payload and count disabled la
|
||||
|
||||
const runner = createGevActionRunner({ viewer, dataManager, styleManager });
|
||||
const voice = await runner('get_current_view_state');
|
||||
assert.deepEqual(voice, packet);
|
||||
assert.deepEqual(withoutVoicePresentation(voice), packet);
|
||||
});
|
||||
|
||||
test('World Packet fingerprints distinguish enabled state but ignore narrative churn', () => {
|
||||
const { viewer, dataManager, styleManager } = harness();
|
||||
const first = getWorldPacket({ viewer, dataManager, styleManager });
|
||||
const prose = structuredClone(first);
|
||||
prose.feedProvenance.note = 'new narration only';
|
||||
assert.equal(
|
||||
semanticWorldPacketFingerprint(first),
|
||||
semanticWorldPacketFingerprint(prose),
|
||||
);
|
||||
const hidden = structuredClone(first);
|
||||
hidden.disabledLayerCount += 1;
|
||||
assert.notEqual(
|
||||
semanticWorldPacketFingerprint(first),
|
||||
semanticWorldPacketFingerprint(hidden),
|
||||
);
|
||||
test('World Packet rejects null options with the camera requirement', () => {
|
||||
assert.throws(() => getWorldPacket(null), /requires a viewer camera/);
|
||||
});
|
||||
Reference in new issue
Block a user