@m1kad0/hannah-proto 2.1.0 → 3.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  Protobuf/gRPC schema definitions for the Hannah voice assistant ecosystem (Core, satellites, WebUI, ioBroker adapter, Telegram bot, and other consumers). This repo is the single source of truth for the wire protocol shared across all of them.
4
4
 
5
- The `.proto` files live flat at the repo root, one per functional area — satellite control, event streaming, the ioBroker agent bridge, user registry, timers, automations, and so on. Nothing here is application code, just schema.
5
+ The `.proto` files live under `hannah/`, one per functional area — satellite control, event streaming, the ioBroker agent bridge, user registry, timers, automations, and so on. Nothing here is application code, just schema.
6
6
 
7
7
  ## Distribution
8
8
 
@@ -20,6 +20,29 @@ Go has no separate package registry, so that tagged GitHub repo *is* the package
20
20
 
21
21
  Alongside the semver package/tag version, every release carries a single-integer `PROTO_VERSION` (see the `PROTO_VERSION` file). Hannah Core and its clients exchange this value on every call and reject a mismatch at runtime — that's the actual compatibility gate, not the semver tag. A breaking schema change requires bumping `PROTO_VERSION`; CI enforces this on every merge request via `buf breaking`.
22
22
 
23
+ ## Per-message compatibility: compat_version
24
+
25
+ `PROTO_VERSION` is repo-wide — any breaking change anywhere bumps it, forcing every consumer to reject, even ones that never call the affected RPC. `compat_version` (`options.proto`) is a finer-grained, independent counter set on an individual message:
26
+
27
+ ```proto
28
+ message Foo {
29
+ option (compat_version) = 2;
30
+ ...
31
+ }
32
+ ```
33
+
34
+ Bump a message's `compat_version` only when *that specific message* has an actual breaking change. A message with no `compat_version` option carries an implicit value of `1` — don't backfill the option onto messages that have never had a breaking change. This lets a consumer-side interceptor check only the messages a given RPC call actually uses instead of rejecting on any unrelated proto change.
35
+
36
+ ## Deprecating fields and RPCs
37
+
38
+ Don't remove a field or RPC the moment it's unused — that forces every consumer to bump immediately, even ones that never touched it (see the `SetGroupRooms` incident that forced 9 unrelated components to bump, `hannah-proto#9`). Instead:
39
+
40
+ 1. Mark it `deprecated = true` (protobuf's built-in field/method option) and note why + what replaces it in a comment.
41
+ 2. Leave it in place until the next planned major cleanup, not the next release.
42
+ 3. Actually remove it (a breaking change, `PROTO_VERSION` bump) only during that cleanup, batched with other accumulated deprecations rather than one at a time.
43
+
44
+ Go/TypeScript/Python codegen surface `deprecated = true` automatically (Go doc comment, TS `@deprecated` JSDoc) — no extra tooling or config needed.
45
+
23
46
  ## Compatibility checks
24
47
 
25
48
  CI runs `buf lint` and `buf breaking` (against `origin/master`) on every MR. To check a local branch against a specific released version:
package/dist/agent.js CHANGED
@@ -3,7 +3,7 @@
3
3
  // versions:
4
4
  // protoc-gen-ts_proto v2.12.0
5
5
  // protoc unknown
6
- // source: agent.proto
6
+ // source: hannah/agent.proto
7
7
  Object.defineProperty(exports, "__esModule", { value: true });
8
8
  exports.AgentSensorUpdate = exports.AgentBleUpdate = exports.AgentResidentAnswered = exports.AgentAskResident = exports.AgentResidentSnapshot = exports.AgentResident = exports.AgentRoomSnapshot = exports.AgentRoom_DisplayNamesEntry = exports.AgentRoom = exports.AgentDeviceSnapshot = exports.AgentDevice_RoomNamesEntry = exports.AgentDevice = exports.AgentStateValue = exports.AgentNotification = exports.AgentTextAnswer = exports.AgentSatelliteDeleted = exports.AgentSatelliteUpdate = exports.AgentSetResidentMood = exports.AgentSetResident = exports.AgentWatchMore = exports.AgentSetState = exports.AgentFirmwareEvent = exports.AgentCommand = exports.AgentTextCommand = exports.AgentStateUpdate = exports.AgentSatelliteControl = exports.AgentMessage = exports.ResidentType = exports.protobufPackage = void 0;
9
9
  /* eslint-disable */
@@ -3,7 +3,7 @@
3
3
  // versions:
4
4
  // protoc-gen-ts_proto v2.12.0
5
5
  // protoc unknown
6
- // source: automation.proto
6
+ // source: hannah/automation.proto
7
7
  Object.defineProperty(exports, "__esModule", { value: true });
8
8
  exports.AutomationStateChanged = exports.AutomationSnapshot = exports.AutomationCommand = exports.AutomationRegister = exports.AutomationMessage = exports.protobufPackage = void 0;
9
9
  /* eslint-disable */
package/dist/car_state.js CHANGED
@@ -3,7 +3,7 @@
3
3
  // versions:
4
4
  // protoc-gen-ts_proto v2.12.0
5
5
  // protoc unknown
6
- // source: car_state.proto
6
+ // source: hannah/car_state.proto
7
7
  Object.defineProperty(exports, "__esModule", { value: true });
8
8
  exports.CarStateProto_WindowsEntry = exports.CarStateProto_DoorsEntry = exports.CarStateProto = exports.GetAllCarStatesResponse = exports.CarStateResponse = exports.protobufPackage = void 0;
9
9
  /* eslint-disable */
@@ -0,0 +1,28 @@
1
+ /**
2
+ * Per-message compatibility check (hannah-proto#9, hannah#217).
3
+ *
4
+ * DRAFT / template — not wired into anything consuming it yet, needs
5
+ * review before use. See python/hannah_proto/compat_interceptor.py for the
6
+ * full design rationale (kept there as the canonical explanation, not
7
+ * repeated here to avoid drift between copies).
8
+ *
9
+ * REQUIRED_COMPAT_VERSIONS comes from generated `./compat_versions`
10
+ * (scripts/gen-compat-versions.js, run by generate.sh) rather than from
11
+ * runtime reflection on the generated message types — ts-proto is
12
+ * configured without `outputSchema`, so there is no descriptor/options
13
+ * data available on the generated types to read compat_version off of
14
+ * directly (unlike the Python/Go versions). See gen-compat-versions.js's
15
+ * header comment for what's unverified there.
16
+ */
17
+ import type { Interceptor } from "@grpc/grpc-js";
18
+ import { DEFAULT_COMPAT_VERSION } from "./compat_versions";
19
+ export declare const COMPAT_VERSION_METADATA_KEY = "x-compat-version";
20
+ export { DEFAULT_COMPAT_VERSION };
21
+ /**
22
+ * grpc-js client interceptor: attaches x-compat-version metadata to every
23
+ * outgoing call, derived from the RPC method being invoked — so the server
24
+ * only has to reject calls genuinely affected by a breaking change, not
25
+ * every call after any unrelated schema change anywhere (see
26
+ * hannah-proto#9, the SetGroupRooms incident).
27
+ */
28
+ export declare const compatVersionInterceptor: Interceptor;
@@ -0,0 +1,30 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.compatVersionInterceptor = exports.DEFAULT_COMPAT_VERSION = exports.COMPAT_VERSION_METADATA_KEY = void 0;
4
+ const grpc_js_1 = require("@grpc/grpc-js");
5
+ const compat_versions_1 = require("./compat_versions");
6
+ Object.defineProperty(exports, "DEFAULT_COMPAT_VERSION", { enumerable: true, get: function () { return compat_versions_1.DEFAULT_COMPAT_VERSION; } });
7
+ exports.COMPAT_VERSION_METADATA_KEY = "x-compat-version";
8
+ /** `methodName` is the bare RPC name (e.g. "SubmitText"), matching the keys
9
+ * gen-compat-versions.js writes into REQUIRED_COMPAT_VERSIONS. */
10
+ function getRequiredCompatVersion(methodName) {
11
+ return compat_versions_1.REQUIRED_COMPAT_VERSIONS[methodName] ?? compat_versions_1.DEFAULT_COMPAT_VERSION;
12
+ }
13
+ /**
14
+ * grpc-js client interceptor: attaches x-compat-version metadata to every
15
+ * outgoing call, derived from the RPC method being invoked — so the server
16
+ * only has to reject calls genuinely affected by a breaking change, not
17
+ * every call after any unrelated schema change anywhere (see
18
+ * hannah-proto#9, the SetGroupRooms incident).
19
+ */
20
+ const compatVersionInterceptor = (options, nextCall) => {
21
+ const methodName = options.method_definition.path.split("/").pop() ?? "";
22
+ const required = getRequiredCompatVersion(methodName);
23
+ return new grpc_js_1.InterceptingCall(nextCall(options), {
24
+ start(metadata, listener, next) {
25
+ metadata.set(exports.COMPAT_VERSION_METADATA_KEY, String(required));
26
+ next(metadata, listener);
27
+ },
28
+ });
29
+ };
30
+ exports.compatVersionInterceptor = compatVersionInterceptor;
@@ -0,0 +1,2 @@
1
+ export declare const DEFAULT_COMPAT_VERSION = 1;
2
+ export declare const REQUIRED_COMPAT_VERSIONS: Record<string, number>;
@@ -0,0 +1,75 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.REQUIRED_COMPAT_VERSIONS = exports.DEFAULT_COMPAT_VERSION = void 0;
4
+ // Generated by scripts/gen-compat-versions.js — do not edit by hand.
5
+ exports.DEFAULT_COMPAT_VERSION = 1;
6
+ exports.REQUIRED_COMPAT_VERSIONS = {
7
+ "GetUsers": 1,
8
+ "GetUser": 1,
9
+ "LinkAccount": 1,
10
+ "UnlinkAccount": 1,
11
+ "SetTrustLevel": 1,
12
+ "SetSystemMessages": 1,
13
+ "SetAutomation": 1,
14
+ "Login": 1,
15
+ "CreateUser": 1,
16
+ "UpdateUser": 1,
17
+ "DeleteUser": 1,
18
+ "GetResidents": 1,
19
+ "GetDevices": 1,
20
+ "ControlDevice": 1,
21
+ "SubmitText": 1,
22
+ "SubmitVoice": 1,
23
+ "Announce": 1,
24
+ "Notify": 1,
25
+ "GetSatellites": 1,
26
+ "SetSatelliteRoom": 1,
27
+ "SetSatelliteDisplayName": 1,
28
+ "SetSatelliteOwner": 1,
29
+ "SetSatelliteSmalltalkFollowup": 1,
30
+ "DeleteSatellite": 1,
31
+ "GetRooms": 1,
32
+ "GetGroups": 1,
33
+ "CreateGroup": 1,
34
+ "UpdateGroup": 1,
35
+ "DeleteGroup": 1,
36
+ "SetGroupSatellites": 1,
37
+ "GetTriggers": 1,
38
+ "CreateTrigger": 1,
39
+ "UpdateTrigger": 1,
40
+ "DeleteTrigger": 1,
41
+ "GetAlarms": 1,
42
+ "CreateAlarm": 1,
43
+ "UpdateAlarm": 1,
44
+ "DeleteAlarm": 1,
45
+ "GetSettings": 1,
46
+ "UpdateConfig": 1,
47
+ "GetBleTags": 1,
48
+ "CreateBleTag": 1,
49
+ "UpdateBleTag": 1,
50
+ "DeleteBleTag": 1,
51
+ "GetCars": 1,
52
+ "CreateCar": 1,
53
+ "UpdateCar": 1,
54
+ "DeleteCar": 1,
55
+ "GetCarState": 1,
56
+ "GetAllCarStates": 1,
57
+ "SubscribeEvents": 1,
58
+ "TriggerFirmwareUpdate": 1,
59
+ "TriggerSatelliteRestart": 1,
60
+ "RequestSatelliteCapture": 1,
61
+ "ReleaseSatelliteCapture": 1,
62
+ "StreamSatelliteAudio": 1,
63
+ "TriggerPlink": 1,
64
+ "RegisterProxy": 1,
65
+ "SubmitSatelliteAudio": 1,
66
+ "NotifySatelliteRegistered": 1,
67
+ "NotifySatelliteGone": 1,
68
+ "ProvisionSatellite": 1,
69
+ "EnrollVoiceprint": 1,
70
+ "TimerConnect": 1,
71
+ "GetTimers": 1,
72
+ "DeleteTimer": 1,
73
+ "AgentConnect": 1,
74
+ "AutomationConnect": 1
75
+ };
package/dist/control.d.ts CHANGED
@@ -46,70 +46,6 @@ export interface AnnounceRequest {
46
46
  /** 0 = not set */
47
47
  userId: number;
48
48
  }
49
- export interface Satellite {
50
- deviceId: string;
51
- /** live-reported room (UDP/proxy registration); empty if not connected */
52
- room: string;
53
- /** IP:port of the UDP satellite; empty if not connected */
54
- address: string;
55
- /** human-readable name from Core DB (empty if not provisioned) */
56
- displayName: string;
57
- /** room assigned in Core DB, "" = unassigned */
58
- roomId: string;
59
- /** display name of room_id */
60
- roomDisplayName: string;
61
- /** UTC "YYYY-MM-DD HH:MM:SS" from Core DB, "" = never seen */
62
- lastSeen: string;
63
- /** true if currently live (UDP or proxy) */
64
- connected: boolean;
65
- /** true if connected and live room differs from room_id */
66
- roomMismatch: boolean;
67
- /** Person (User) assigned as owner in Core DB, 0 = unassigned. #31 */
68
- ownerUserId: number;
69
- /** display name of owner_user_id, "" if unassigned */
70
- ownerDisplayName: string;
71
- /** firmware_version of the satellite */
72
- firmwareVersion: string;
73
- /** true if the device has a new firmware available */
74
- updateAvailable: boolean;
75
- newVersion?: string | undefined;
76
- /** #158: mic reopens after a Smalltalk answer instead of waiting for wake-word/PTT again */
77
- smalltalkFollowupListen: boolean;
78
- }
79
- export interface GetSatellitesResponse {
80
- satellites: Satellite[];
81
- }
82
- export interface SetSatelliteRoomRequest {
83
- deviceId: string;
84
- /** "" = unassign */
85
- roomId: string;
86
- /** requestor */
87
- requestorId: number;
88
- }
89
- export interface SetSatelliteDisplayNameRequest {
90
- deviceId: string;
91
- displayName: string;
92
- /** requestor */
93
- requestorId: number;
94
- }
95
- export interface SetSatelliteOwnerRequest {
96
- deviceId: string;
97
- /** 0 = unassign */
98
- userId: number;
99
- /** requestor */
100
- requestorId: number;
101
- }
102
- export interface SetSatelliteSmalltalkFollowupRequest {
103
- deviceId: string;
104
- enabled: boolean;
105
- /** requestor */
106
- requestorId: number;
107
- }
108
- export interface DeleteSatelliteRequest {
109
- deviceId: string;
110
- /** requestor */
111
- requestorId: number;
112
- }
113
49
  export interface Room {
114
50
  roomId: string;
115
51
  displayName: string;
@@ -341,13 +277,6 @@ export declare const SubmitTextResponse: MessageFns<SubmitTextResponse>;
341
277
  export declare const SubmitVoiceRequest: MessageFns<SubmitVoiceRequest>;
342
278
  export declare const SubmitVoiceResponse: MessageFns<SubmitVoiceResponse>;
343
279
  export declare const AnnounceRequest: MessageFns<AnnounceRequest>;
344
- export declare const Satellite: MessageFns<Satellite>;
345
- export declare const GetSatellitesResponse: MessageFns<GetSatellitesResponse>;
346
- export declare const SetSatelliteRoomRequest: MessageFns<SetSatelliteRoomRequest>;
347
- export declare const SetSatelliteDisplayNameRequest: MessageFns<SetSatelliteDisplayNameRequest>;
348
- export declare const SetSatelliteOwnerRequest: MessageFns<SetSatelliteOwnerRequest>;
349
- export declare const SetSatelliteSmalltalkFollowupRequest: MessageFns<SetSatelliteSmalltalkFollowupRequest>;
350
- export declare const DeleteSatelliteRequest: MessageFns<DeleteSatelliteRequest>;
351
280
  export declare const Room: MessageFns<Room>;
352
281
  export declare const GetRoomsResponse: MessageFns<GetRoomsResponse>;
353
282
  export declare const GroupSatellite: MessageFns<GroupSatellite>;