instar 1.3.1637 → 1.3.1639

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.
Files changed (66) hide show
  1. package/dist/commands/server.d.ts.map +1 -1
  2. package/dist/commands/server.js +22 -8
  3. package/dist/commands/server.js.map +1 -1
  4. package/dist/core/DesktopHost.d.ts +4 -2
  5. package/dist/core/DesktopHost.d.ts.map +1 -1
  6. package/dist/core/DesktopHost.js.map +1 -1
  7. package/dist/core/DesktopProbeReadiness.d.ts +1 -0
  8. package/dist/core/DesktopProbeReadiness.d.ts.map +1 -1
  9. package/dist/core/DesktopProbeReadiness.js +11 -2
  10. package/dist/core/DesktopProbeReadiness.js.map +1 -1
  11. package/dist/core/HyprlandDesktopHost.d.ts +150 -0
  12. package/dist/core/HyprlandDesktopHost.d.ts.map +1 -0
  13. package/dist/core/HyprlandDesktopHost.js +569 -0
  14. package/dist/core/HyprlandDesktopHost.js.map +1 -0
  15. package/dist/core/LinuxDesktopHost.d.ts +1 -0
  16. package/dist/core/LinuxDesktopHost.d.ts.map +1 -1
  17. package/dist/core/LinuxDesktopHost.js +7 -4
  18. package/dist/core/LinuxDesktopHost.js.map +1 -1
  19. package/dist/core/SessionDrainRunner.d.ts +4 -2
  20. package/dist/core/SessionDrainRunner.d.ts.map +1 -1
  21. package/dist/core/SessionDrainRunner.js +2 -2
  22. package/dist/core/SessionDrainRunner.js.map +1 -1
  23. package/dist/core/TopicProfileOrchestrator.d.ts.map +1 -1
  24. package/dist/core/TopicProfileOrchestrator.js +2 -1
  25. package/dist/core/TopicProfileOrchestrator.js.map +1 -1
  26. package/dist/core/WaylandVirtualPointer.d.ts +31 -0
  27. package/dist/core/WaylandVirtualPointer.d.ts.map +1 -0
  28. package/dist/core/WaylandVirtualPointer.js +171 -0
  29. package/dist/core/WaylandVirtualPointer.js.map +1 -0
  30. package/dist/data/standards-guard-index.json +1 -1
  31. package/dist/data/standards-guard-index.meta.json +2 -2
  32. package/dist/data/standards-registry.meta.json +1 -1
  33. package/dist/messaging/plainNoticeCheck.d.ts +9 -0
  34. package/dist/messaging/plainNoticeCheck.d.ts.map +1 -0
  35. package/dist/messaging/plainNoticeCheck.js +68 -0
  36. package/dist/messaging/plainNoticeCheck.js.map +1 -0
  37. package/dist/messaging/shared/isSystemOrProxyMessage.d.ts +0 -24
  38. package/dist/messaging/shared/isSystemOrProxyMessage.d.ts.map +1 -1
  39. package/dist/messaging/shared/isSystemOrProxyMessage.js +6 -3
  40. package/dist/messaging/shared/isSystemOrProxyMessage.js.map +1 -1
  41. package/dist/messaging/userNotices.d.ts +47 -0
  42. package/dist/messaging/userNotices.d.ts.map +1 -0
  43. package/dist/messaging/userNotices.js +79 -0
  44. package/dist/messaging/userNotices.js.map +1 -0
  45. package/dist/native/InstarWedgeCanary.app/Contents/MacOS/InstarWedgeCanary +0 -0
  46. package/dist/native/instar-desktop-host +0 -0
  47. package/dist/scaffold/templates.d.ts.map +1 -1
  48. package/dist/scaffold/templates.js +2 -1
  49. package/dist/scaffold/templates.js.map +1 -1
  50. package/dist/server/AgentServer.js +4 -4
  51. package/dist/server/AgentServer.js.map +1 -1
  52. package/package.json +4 -3
  53. package/scripts/lint-plain-user-notices.mjs +191 -0
  54. package/scripts/plain-user-notices-baseline.json +6 -0
  55. package/src/data/builtin-manifest.json +3 -3
  56. package/src/data/plain-notice-terms.json +38 -0
  57. package/src/data/standards-guard-index.json +1 -1
  58. package/src/data/standards-guard-index.meta.json +2 -2
  59. package/src/data/standards-registry.meta.json +1 -1
  60. package/src/scaffold/templates.ts +2 -1
  61. package/upgrades/1.3.1638.md +21 -0
  62. package/upgrades/1.3.1639.md +24 -0
  63. package/upgrades/side-effects/hyprland-remote-desktop.eli16.md +39 -0
  64. package/upgrades/side-effects/hyprland-remote-desktop.md +91 -0
  65. package/upgrades/side-effects/plain-user-notices.eli16.md +15 -0
  66. package/upgrades/side-effects/plain-user-notices.md +76 -0
@@ -0,0 +1,171 @@
1
+ /** A virtual mouse on a Wayland compositor, spoken directly over its socket
2
+ * (`zwlr_virtual_pointer_v1`). No package and no root: the compositor offers it
3
+ * to its own user's programs, and Hyprland, Sway and other wlroots desktops
4
+ * implement it. ydotool would need /dev/uinput; wlrctl is not in Arch's repos.
5
+ *
6
+ * Only the tiny part of the Wayland wire format this needs is here: 32-bit
7
+ * little-endian words, messages of [object id, size << 16 | opcode, args],
8
+ * strings as length + bytes + NUL padded to 4. */
9
+ import net from 'node:net';
10
+ const DISPLAY = 1;
11
+ const REGISTRY = 2;
12
+ const SYNC_CALLBACK = 3;
13
+ const MANAGER = 4;
14
+ const POINTER = 5;
15
+ const MANAGER_INTERFACE = 'zwlr_virtual_pointer_manager_v1';
16
+ const CONNECT_TIMEOUT_MS = 3_000;
17
+ /** Fixed-point pixels of scroll per wheel click, the usual wlroots step. */
18
+ const AXIS_STEP = 15;
19
+ /** Linux input event codes. */
20
+ export const POINTER_BUTTONS = { left: 272, right: 273, middle: 274 };
21
+ /** One request on the wire. Exported so tests can read back what was sent. */
22
+ export function encodeWaylandMessage(objectId, opcode, args) {
23
+ const parts = [];
24
+ for (const arg of args) {
25
+ if (typeof arg === 'string') {
26
+ const text = Buffer.from(`${arg}\0`);
27
+ const length = Buffer.alloc(4);
28
+ length.writeUInt32LE(text.length);
29
+ parts.push(length, text, Buffer.alloc((4 - (text.length % 4)) % 4));
30
+ }
31
+ else {
32
+ const word = Buffer.alloc(4);
33
+ word.writeUInt32LE(arg >>> 0);
34
+ parts.push(word);
35
+ }
36
+ }
37
+ const body = Buffer.concat(parts);
38
+ const header = Buffer.alloc(8);
39
+ header.writeUInt32LE(objectId, 0);
40
+ header.writeUInt32LE(((8 + body.length) << 16) | opcode, 4);
41
+ return Buffer.concat([header, body]);
42
+ }
43
+ /** Wayland's 24.8 fixed-point number. */
44
+ function fixed(value) {
45
+ return Math.round(value * 256);
46
+ }
47
+ export class WaylandVirtualPointer {
48
+ socketPath;
49
+ onLost;
50
+ socket = null;
51
+ inbox = Buffer.alloc(0);
52
+ ready = false;
53
+ closed = false;
54
+ constructor(socketPath, onLost = () => { }) {
55
+ this.socketPath = socketPath;
56
+ this.onLost = onLost;
57
+ }
58
+ get alive() {
59
+ return this.ready && !this.closed;
60
+ }
61
+ /** Rejects when the socket is gone or this compositor has no virtual pointer. */
62
+ connect() {
63
+ return new Promise((resolve, reject) => {
64
+ const socket = net.connect(this.socketPath);
65
+ this.socket = socket;
66
+ let manager = null;
67
+ let settled = false;
68
+ const finish = (error) => {
69
+ if (settled)
70
+ return;
71
+ settled = true;
72
+ clearTimeout(timer);
73
+ if (error) {
74
+ this.close();
75
+ reject(error);
76
+ }
77
+ else {
78
+ this.ready = true;
79
+ resolve();
80
+ }
81
+ };
82
+ const timer = setTimeout(() => finish(new Error('wayland-pointer-connect-timeout')), CONNECT_TIMEOUT_MS);
83
+ timer.unref?.();
84
+ socket.on('connect', () => {
85
+ // wl_display.get_registry, then wl_display.sync so the compositor says
86
+ // when it has finished listing what it offers.
87
+ socket.write(encodeWaylandMessage(DISPLAY, 1, [REGISTRY]));
88
+ socket.write(encodeWaylandMessage(DISPLAY, 0, [SYNC_CALLBACK]));
89
+ });
90
+ socket.on('data', chunk => {
91
+ this.inbox = Buffer.concat([this.inbox, chunk]);
92
+ while (this.inbox.length >= 8) {
93
+ const objectId = this.inbox.readUInt32LE(0);
94
+ const word = this.inbox.readUInt32LE(4);
95
+ const size = word >>> 16;
96
+ const opcode = word & 0xffff;
97
+ if (size < 8) {
98
+ finish(new Error('wayland-pointer-protocol-error'));
99
+ return;
100
+ }
101
+ if (this.inbox.length < size)
102
+ break;
103
+ const body = this.inbox.subarray(8, size);
104
+ if (objectId === DISPLAY && opcode === 0) {
105
+ finish(new Error('wayland-pointer-compositor-error'));
106
+ this.lose();
107
+ return;
108
+ }
109
+ if (objectId === REGISTRY && opcode === 0 && body.length >= 12) {
110
+ const length = body.readUInt32LE(4);
111
+ const name = body.toString('utf8', 8, Math.min(body.length, 8 + Math.max(0, length - 1)));
112
+ if (name === MANAGER_INTERFACE)
113
+ manager = { name: body.readUInt32LE(0) };
114
+ }
115
+ if (objectId === SYNC_CALLBACK && opcode === 0) {
116
+ if (!manager) {
117
+ finish(new Error('wayland-pointer-unavailable'));
118
+ return;
119
+ }
120
+ socket.write(encodeWaylandMessage(REGISTRY, 0, [manager.name, MANAGER_INTERFACE, 1, MANAGER]));
121
+ // create_virtual_pointer(seat = null: the default seat, new id).
122
+ socket.write(encodeWaylandMessage(MANAGER, 0, [0, POINTER]));
123
+ finish();
124
+ }
125
+ this.inbox = this.inbox.subarray(size);
126
+ }
127
+ });
128
+ socket.on('error', error => { finish(error); this.lose(); });
129
+ socket.on('close', () => { finish(new Error('wayland-pointer-closed')); this.lose(); });
130
+ });
131
+ }
132
+ lose() {
133
+ if (this.closed)
134
+ return;
135
+ this.closed = true;
136
+ this.ready = false;
137
+ this.onLost();
138
+ }
139
+ send(opcode, args) {
140
+ if (!this.alive || !this.socket)
141
+ throw new Error('wayland-pointer-not-connected');
142
+ this.socket.write(encodeWaylandMessage(POINTER, opcode, args));
143
+ }
144
+ time() {
145
+ return Date.now() & 0x7fffffff;
146
+ }
147
+ /** x and y within extentWidth × extentHeight, the size of the whole layout. */
148
+ moveAbsolute(x, y, extentWidth, extentHeight) {
149
+ this.send(1, [this.time(), Math.max(0, Math.round(x)), Math.max(0, Math.round(y)), extentWidth, extentHeight]);
150
+ this.send(4, []); // frame
151
+ }
152
+ button(code, pressed) {
153
+ this.send(2, [this.time(), code, pressed ? 1 : 0]);
154
+ this.send(4, []);
155
+ }
156
+ /** Whole wheel clicks. Positive is down or right. */
157
+ scroll(axis, clicks) {
158
+ const which = axis === 'vertical' ? 0 : 1;
159
+ this.send(5, [0]); // axis_source: wheel
160
+ this.send(7, [this.time(), which, fixed(clicks * AXIS_STEP), clicks]); // axis_discrete
161
+ this.send(4, []);
162
+ }
163
+ close() {
164
+ this.closed = true;
165
+ this.ready = false;
166
+ // end, not destroy: a release sent just before this must still reach the compositor.
167
+ this.socket?.end();
168
+ this.socket = null;
169
+ }
170
+ }
171
+ //# sourceMappingURL=WaylandVirtualPointer.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"WaylandVirtualPointer.js","sourceRoot":"","sources":["../../src/core/WaylandVirtualPointer.ts"],"names":[],"mappings":"AAAA;;;;;;;kDAOkD;AAClD,OAAO,GAAG,MAAM,UAAU,CAAC;AAE3B,MAAM,OAAO,GAAG,CAAC,CAAC;AAClB,MAAM,QAAQ,GAAG,CAAC,CAAC;AACnB,MAAM,aAAa,GAAG,CAAC,CAAC;AACxB,MAAM,OAAO,GAAG,CAAC,CAAC;AAClB,MAAM,OAAO,GAAG,CAAC,CAAC;AAElB,MAAM,iBAAiB,GAAG,iCAAiC,CAAC;AAC5D,MAAM,kBAAkB,GAAG,KAAK,CAAC;AACjC,4EAA4E;AAC5E,MAAM,SAAS,GAAG,EAAE,CAAC;AAErB,+BAA+B;AAC/B,MAAM,CAAC,MAAM,eAAe,GAAG,EAAE,IAAI,EAAE,GAAG,EAAE,KAAK,EAAE,GAAG,EAAE,MAAM,EAAE,GAAG,EAAW,CAAC;AAI/E,8EAA8E;AAC9E,MAAM,UAAU,oBAAoB,CAAC,QAAgB,EAAE,MAAc,EAAE,IAAkB;IACvF,MAAM,KAAK,GAAa,EAAE,CAAC;IAC3B,KAAK,MAAM,GAAG,IAAI,IAAI,EAAE,CAAC;QACvB,IAAI,OAAO,GAAG,KAAK,QAAQ,EAAE,CAAC;YAC5B,MAAM,IAAI,GAAG,MAAM,CAAC,IAAI,CAAC,GAAG,GAAG,IAAI,CAAC,CAAC;YACrC,MAAM,MAAM,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;YAC/B,MAAM,CAAC,aAAa,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;YAClC,KAAK,CAAC,IAAI,CAAC,MAAM,EAAE,IAAI,EAAE,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,IAAI,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;QACtE,CAAC;aAAM,CAAC;YACN,MAAM,IAAI,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;YAC7B,IAAI,CAAC,aAAa,CAAC,GAAG,KAAK,CAAC,CAAC,CAAC;YAC9B,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACnB,CAAC;IACH,CAAC;IACD,MAAM,IAAI,GAAG,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;IAClC,MAAM,MAAM,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;IAC/B,MAAM,CAAC,aAAa,CAAC,QAAQ,EAAE,CAAC,CAAC,CAAC;IAClC,MAAM,CAAC,aAAa,CAAC,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC,GAAG,MAAM,EAAE,CAAC,CAAC,CAAC;IAC5D,OAAO,MAAM,CAAC,MAAM,CAAC,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC,CAAC;AACvC,CAAC;AAED,yCAAyC;AACzC,SAAS,KAAK,CAAC,KAAa;IAC1B,OAAO,IAAI,CAAC,KAAK,CAAC,KAAK,GAAG,GAAG,CAAC,CAAC;AACjC,CAAC;AAED,MAAM,OAAO,qBAAqB;IAMH,UAAU;IAA2B,MAAM;IALhE,MAAM,GAAsB,IAAI,CAAC;IACjC,KAAK,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;IACxB,KAAK,GAAG,KAAK,CAAC;IACd,MAAM,GAAG,KAAK,CAAC;IAEvB,YAA6B,UAAkB,EAAmB,MAAM,GAAe,GAAG,EAAE,GAAE,CAAC;0BAAlE,UAAU;sBAA2B,MAAM;IAA0B,CAAC;IAEnG,IAAI,KAAK;QACP,OAAO,IAAI,CAAC,KAAK,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC;IACpC,CAAC;IAED,iFAAiF;IACjF,OAAO;QACL,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;YACrC,MAAM,MAAM,GAAG,GAAG,CAAC,OAAO,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;YAC5C,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;YACrB,IAAI,OAAO,GAA4B,IAAI,CAAC;YAC5C,IAAI,OAAO,GAAG,KAAK,CAAC;YACpB,MAAM,MAAM,GAAG,CAAC,KAAa,EAAE,EAAE;gBAC/B,IAAI,OAAO;oBAAE,OAAO;gBACpB,OAAO,GAAG,IAAI,CAAC;gBACf,YAAY,CAAC,KAAK,CAAC,CAAC;gBACpB,IAAI,KAAK,EAAE,CAAC;oBAAC,IAAI,CAAC,KAAK,EAAE,CAAC;oBAAC,MAAM,CAAC,KAAK,CAAC,CAAC;gBAAC,CAAC;qBAAM,CAAC;oBAAC,IAAI,CAAC,KAAK,GAAG,IAAI,CAAC;oBAAC,OAAO,EAAE,CAAC;gBAAC,CAAC;YACpF,CAAC,CAAC;YACF,MAAM,KAAK,GAAG,UAAU,CAAC,GAAG,EAAE,CAAC,MAAM,CAAC,IAAI,KAAK,CAAC,iCAAiC,CAAC,CAAC,EAAE,kBAAkB,CAAC,CAAC;YACzG,KAAK,CAAC,KAAK,EAAE,EAAE,CAAC;YAChB,MAAM,CAAC,EAAE,CAAC,SAAS,EAAE,GAAG,EAAE;gBACxB,uEAAuE;gBACvE,+CAA+C;gBAC/C,MAAM,CAAC,KAAK,CAAC,oBAAoB,CAAC,OAAO,EAAE,CAAC,EAAE,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC;gBAC3D,MAAM,CAAC,KAAK,CAAC,oBAAoB,CAAC,OAAO,EAAE,CAAC,EAAE,CAAC,aAAa,CAAC,CAAC,CAAC,CAAC;YAClE,CAAC,CAAC,CAAC;YACH,MAAM,CAAC,EAAE,CAAC,MAAM,EAAE,KAAK,CAAC,EAAE;gBACxB,IAAI,CAAC,KAAK,GAAG,MAAM,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,KAAK,EAAE,KAAK,CAAC,CAAC,CAAC;gBAChD,OAAO,IAAI,CAAC,KAAK,CAAC,MAAM,IAAI,CAAC,EAAE,CAAC;oBAC9B,MAAM,QAAQ,GAAG,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC;oBAC5C,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC;oBACxC,MAAM,IAAI,GAAG,IAAI,KAAK,EAAE,CAAC;oBACzB,MAAM,MAAM,GAAG,IAAI,GAAG,MAAM,CAAC;oBAC7B,IAAI,IAAI,GAAG,CAAC,EAAE,CAAC;wBAAC,MAAM,CAAC,IAAI,KAAK,CAAC,gCAAgC,CAAC,CAAC,CAAC;wBAAC,OAAO;oBAAC,CAAC;oBAC9E,IAAI,IAAI,CAAC,KAAK,CAAC,MAAM,GAAG,IAAI;wBAAE,MAAM;oBACpC,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC,EAAE,IAAI,CAAC,CAAC;oBAC1C,IAAI,QAAQ,KAAK,OAAO,IAAI,MAAM,KAAK,CAAC,EAAE,CAAC;wBACzC,MAAM,CAAC,IAAI,KAAK,CAAC,kCAAkC,CAAC,CAAC,CAAC;wBACtD,IAAI,CAAC,IAAI,EAAE,CAAC;wBACZ,OAAO;oBACT,CAAC;oBACD,IAAI,QAAQ,KAAK,QAAQ,IAAI,MAAM,KAAK,CAAC,IAAI,IAAI,CAAC,MAAM,IAAI,EAAE,EAAE,CAAC;wBAC/D,MAAM,MAAM,GAAG,IAAI,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC;wBACpC,MAAM,IAAI,GAAG,IAAI,CAAC,QAAQ,CAAC,MAAM,EAAE,CAAC,EAAE,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,MAAM,EAAE,CAAC,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;wBAC1F,IAAI,IAAI,KAAK,iBAAiB;4BAAE,OAAO,GAAG,EAAE,IAAI,EAAE,IAAI,CAAC,YAAY,CAAC,CAAC,CAAC,EAAE,CAAC;oBAC3E,CAAC;oBACD,IAAI,QAAQ,KAAK,aAAa,IAAI,MAAM,KAAK,CAAC,EAAE,CAAC;wBAC/C,IAAI,CAAC,OAAO,EAAE,CAAC;4BAAC,MAAM,CAAC,IAAI,KAAK,CAAC,6BAA6B,CAAC,CAAC,CAAC;4BAAC,OAAO;wBAAC,CAAC;wBAC3E,MAAM,CAAC,KAAK,CAAC,oBAAoB,CAAC,QAAQ,EAAE,CAAC,EAAE,CAAC,OAAO,CAAC,IAAI,EAAE,iBAAiB,EAAE,CAAC,EAAE,OAAO,CAAC,CAAC,CAAC,CAAC;wBAC/F,iEAAiE;wBACjE,MAAM,CAAC,KAAK,CAAC,oBAAoB,CAAC,OAAO,EAAE,CAAC,EAAE,CAAC,CAAC,EAAE,OAAO,CAAC,CAAC,CAAC,CAAC;wBAC7D,MAAM,EAAE,CAAC;oBACX,CAAC;oBACD,IAAI,CAAC,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC;gBACzC,CAAC;YACH,CAAC,CAAC,CAAC;YACH,MAAM,CAAC,EAAE,CAAC,OAAO,EAAE,KAAK,CAAC,EAAE,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC;YAC7D,MAAM,CAAC,EAAE,CAAC,OAAO,EAAE,GAAG,EAAE,GAAG,MAAM,CAAC,IAAI,KAAK,CAAC,wBAAwB,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC;QAC1F,CAAC,CAAC,CAAC;IACL,CAAC;IAEO,IAAI;QACV,IAAI,IAAI,CAAC,MAAM;YAAE,OAAO;QACxB,IAAI,CAAC,MAAM,GAAG,IAAI,CAAC;QACnB,IAAI,CAAC,KAAK,GAAG,KAAK,CAAC;QACnB,IAAI,CAAC,MAAM,EAAE,CAAC;IAChB,CAAC;IAEO,IAAI,CAAC,MAAc,EAAE,IAAkB;QAC7C,IAAI,CAAC,IAAI,CAAC,KAAK,IAAI,CAAC,IAAI,CAAC,MAAM;YAAE,MAAM,IAAI,KAAK,CAAC,+BAA+B,CAAC,CAAC;QAClF,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,oBAAoB,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,CAAC,CAAC,CAAC;IACjE,CAAC;IAEO,IAAI;QACV,OAAO,IAAI,CAAC,GAAG,EAAE,GAAG,UAAU,CAAC;IACjC,CAAC;IAED,+EAA+E;IAC/E,YAAY,CAAC,CAAS,EAAE,CAAS,EAAE,WAAmB,EAAE,YAAoB;QAC1E,IAAI,CAAC,IAAI,CAAC,CAAC,EAAE,CAAC,IAAI,CAAC,IAAI,EAAE,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,WAAW,EAAE,YAAY,CAAC,CAAC,CAAC;QAC/G,IAAI,CAAC,IAAI,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ;IAC5B,CAAC;IAED,MAAM,CAAC,IAAY,EAAE,OAAgB;QACnC,IAAI,CAAC,IAAI,CAAC,CAAC,EAAE,CAAC,IAAI,CAAC,IAAI,EAAE,EAAE,IAAI,EAAE,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;QACnD,IAAI,CAAC,IAAI,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;IACnB,CAAC;IAED,qDAAqD;IACrD,MAAM,CAAC,IAA+B,EAAE,MAAc;QACpD,MAAM,KAAK,GAAG,IAAI,KAAK,UAAU,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;QAC1C,IAAI,CAAC,IAAI,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,qBAAqB;QACxC,IAAI,CAAC,IAAI,CAAC,CAAC,EAAE,CAAC,IAAI,CAAC,IAAI,EAAE,EAAE,KAAK,EAAE,KAAK,CAAC,MAAM,GAAG,SAAS,CAAC,EAAE,MAAM,CAAC,CAAC,CAAC,CAAC,gBAAgB;QACvF,IAAI,CAAC,IAAI,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;IACnB,CAAC;IAED,KAAK;QACH,IAAI,CAAC,MAAM,GAAG,IAAI,CAAC;QACnB,IAAI,CAAC,KAAK,GAAG,KAAK,CAAC;QACnB,qFAAqF;QACrF,IAAI,CAAC,MAAM,EAAE,GAAG,EAAE,CAAC;QACnB,IAAI,CAAC,MAAM,GAAG,IAAI,CAAC;IACrB,CAAC;CACF"}
@@ -2,7 +2,7 @@
2
2
  "schemaVersion": 1,
3
3
  "generatedFrom": "source-tree",
4
4
  "registrySha256": "db01d9f92c6aa26ca903702896b41be53722982ed07d27633237bb254c214290",
5
- "packageVersion": "1.3.1637",
5
+ "packageVersion": "1.3.1639",
6
6
  "guards": [
7
7
  {
8
8
  "ref": "docs/audits/phase-b/f10-triage.md",
@@ -1,5 +1,5 @@
1
1
  {
2
- "sha256": "30133cdb772869d1fd25e4cff1174c742044ea3297758c6b1cf8e26349ed3d60",
2
+ "sha256": "c31cb53ab5cc37a07500f2de39cc6ec13bade8afb52f76074876e04ca68a2d6b",
3
3
  "registrySha256": "db01d9f92c6aa26ca903702896b41be53722982ed07d27633237bb254c214290",
4
- "packageVersion": "1.3.1637"
4
+ "packageVersion": "1.3.1639"
5
5
  }
@@ -2,5 +2,5 @@
2
2
  "sha256": "db01d9f92c6aa26ca903702896b41be53722982ed07d27633237bb254c214290",
3
3
  "articleCount": 93,
4
4
  "generatedFrom": "docs/STANDARDS-REGISTRY.md",
5
- "packageVersion": "1.3.1637"
5
+ "packageVersion": "1.3.1639"
6
6
  }
@@ -0,0 +1,9 @@
1
+ export interface PlainNoticeProblem {
2
+ code: string;
3
+ match: string;
4
+ why: string;
5
+ }
6
+ /** Below this, a notice cannot carry what happened + what it means + what to do. */
7
+ export declare const MIN_WORDS = 6;
8
+ export declare function findPlainNoticeProblems(text: string): PlainNoticeProblem[];
9
+ //# sourceMappingURL=plainNoticeCheck.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"plainNoticeCheck.d.ts","sourceRoot":"","sources":["../../src/messaging/plainNoticeCheck.ts"],"names":[],"mappings":"AAqBA,MAAM,WAAW,kBAAkB;IACjC,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,GAAG,EAAE,MAAM,CAAC;CACb;AAED,oFAAoF;AACpF,eAAO,MAAM,SAAS,IAAI,CAAC;AA2B3B,wBAAgB,uBAAuB,CAAC,IAAI,EAAE,MAAM,GAAG,kBAAkB,EAAE,CAqB1E"}
@@ -0,0 +1,68 @@
1
+ /**
2
+ * plainNoticeCheck — deterministic plain-language check for fixed notices the
3
+ * server writes to the user (docs/UX-AND-AGENT-AGENCY-STANDARD.md Rule 8).
4
+ *
5
+ * SIGNAL ONLY, used at TEST time over USER_NOTICE_CATALOG (and its term list
6
+ * at build time by scripts/lint-plain-user-notices.mjs). It never runs on the
7
+ * live send path: these notices travel deterministic delivery paths that must
8
+ * not be held or rewritten at runtime (an alarm has to arrive when the model
9
+ * is down). The check is brittle on purpose — literal tokens — which is fine
10
+ * for a detector feeding a test, and would be wrong for a runtime authority.
11
+ *
12
+ * It covers the TOO-TECHNICAL side mechanically (internal words, code
13
+ * identifiers, raw reason codes, ids, file paths) and the TOO-VAGUE side only
14
+ * as a floor (a notice shorter than MIN_WORDS cannot say what happened, what
15
+ * it means and what to do). Judging "enough information" beyond that floor is
16
+ * for the reviewer; Rule 8's before/after examples are the reference.
17
+ */
18
+ import fs from 'node:fs';
19
+ import { detectInternalIdLeak } from '../core/internal-id-leak.js';
20
+ import { detectRawFilePath } from '../core/raw-file-path.js';
21
+ /** Below this, a notice cannot carry what happened + what it means + what to do. */
22
+ export const MIN_WORDS = 6;
23
+ // One term list for this check and the build-time lint (which cannot import
24
+ // TypeScript). '../../src/data' resolves from both src/messaging and dist/messaging.
25
+ const terms = JSON.parse(fs.readFileSync(new URL('../../src/data/plain-notice-terms.json', import.meta.url), 'utf-8'));
26
+ function escapeRe(s) {
27
+ return s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
28
+ }
29
+ const TERM_RES = terms.internalTerms.map((t) => ({
30
+ term: t,
31
+ re: new RegExp(`(^|[^A-Za-z0-9_-])${escapeRe(t)}(?![A-Za-z0-9_-])`, t === t.toUpperCase() && t.length <= 4 ? '' : 'i'),
32
+ }));
33
+ const CODE_RES = terms.codePatterns.map((p) => ({
34
+ ...p,
35
+ rx: new RegExp(p.re, 'g'),
36
+ }));
37
+ const CAMEL_ALLOW = new Set(terms.camelCaseAllow.map((s) => s.toLowerCase()));
38
+ /** Strip Telegram HTML tags and emoji so they don't count as words or identifiers. */
39
+ function visibleText(text) {
40
+ return text.replace(/<[^>]+>/g, ' ');
41
+ }
42
+ export function findPlainNoticeProblems(text) {
43
+ const out = [];
44
+ const t = visibleText(text ?? '');
45
+ const words = t.split(/\s+/).filter((w) => /[A-Za-z]/.test(w));
46
+ if (words.length < MIN_WORDS) {
47
+ out.push({ code: 'TOO_SHORT', match: t.trim(), why: `under ${MIN_WORDS} words cannot say what happened, what it means and what to do` });
48
+ }
49
+ for (const { term, re } of TERM_RES) {
50
+ if (re.test(t))
51
+ out.push({ code: 'INTERNAL_TERM', match: term, why: 'an internal word the person cannot act on' });
52
+ }
53
+ for (const p of CODE_RES) {
54
+ for (const m of t.matchAll(p.rx)) {
55
+ if (p.code === 'CAMEL_CASE' && CAMEL_ALLOW.has(m[0].toLowerCase()))
56
+ continue;
57
+ out.push({ code: p.code, match: m[0], why: p.why });
58
+ }
59
+ }
60
+ const leak = detectInternalIdLeak(t);
61
+ for (const term of leak.terms)
62
+ out.push({ code: 'INTERNAL_ID', match: term, why: 'an internal id or component name' });
63
+ const fp = detectRawFilePath(t);
64
+ if (fp.detected)
65
+ out.push({ code: 'FILE_PATH', match: fp.match ?? '', why: 'a raw file path' });
66
+ return out;
67
+ }
68
+ //# sourceMappingURL=plainNoticeCheck.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"plainNoticeCheck.js","sourceRoot":"","sources":["../../src/messaging/plainNoticeCheck.ts"],"names":[],"mappings":"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"}
@@ -1,27 +1,3 @@
1
- /**
2
- * isSystemOrProxyMessage — single source of truth for classifying outbound
3
- * messages that are NOT real agent responses.
4
- *
5
- * The agent's channel to the user carries three kinds of from-agent traffic:
6
- * 1. Real agent responses (substantive replies).
7
- * 2. Delivery/session lifecycle acknowledgements ("✓ Delivered",
8
- * "Session respawned.", etc.) emitted by the server itself.
9
- * 3. PresenceProxy standby messages, prefixed "🔭", generated by a
10
- * monitoring LLM while the agent is busy.
11
- *
12
- * Several subsystems need to distinguish (2)+(3) from (1):
13
- * - CompactionSentinel's recoverFn: must re-inject if the real agent
14
- * never answered, even if a proxy message was sent.
15
- * - PresenceProxy race guard: must not self-cancel on a sibling proxy
16
- * message or delivery ack.
17
- * - Any "has the agent responded since X" log scan.
18
- *
19
- * Before this helper the prefix list was duplicated across PresenceProxy
20
- * and server.ts (checkLogForAgentResponse) and MISSING from
21
- * recoverCompactedSession — which is what let the compaction-recovery
22
- * path silently decline when the only from-agent message since the user's
23
- * question was a PresenceProxy standby.
24
- */
25
1
  export declare function isSystemOrProxyMessage(text: string | null | undefined): boolean;
26
2
  /**
27
3
  * findLastRealMessage — walks a chronological message history backward,
@@ -1 +1 @@
1
- {"version":3,"file":"isSystemOrProxyMessage.d.ts","sourceRoot":"","sources":["../../../src/messaging/shared/isSystemOrProxyMessage.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,wBAAgB,sBAAsB,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GAAG,OAAO,CAc/E;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,mBAAmB,CAAC,CAAC,SAAS;IAAE,IAAI,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;CAAE,EACpE,OAAO,EAAE,aAAa,CAAC,CAAC,CAAC,GACxB,CAAC,GAAG,IAAI,CAOV"}
1
+ {"version":3,"file":"isSystemOrProxyMessage.d.ts","sourceRoot":"","sources":["../../../src/messaging/shared/isSystemOrProxyMessage.ts"],"names":[],"mappings":"AA0BA,wBAAgB,sBAAsB,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GAAG,OAAO,CAa/E;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,mBAAmB,CAAC,CAAC,SAAS;IAAE,IAAI,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;CAAE,EACpE,OAAO,EAAE,aAAa,CAAC,CAAC,CAAC,GACxB,CAAC,GAAG,IAAI,CAOV"}
@@ -22,6 +22,7 @@
22
22
  * path silently decline when the only from-agent message since the user's
23
23
  * question was a PresenceProxy standby.
24
24
  */
25
+ import { LIFECYCLE_NOTICE_EXACT, LIFECYCLE_NOTICE_PREFIXES } from '../userNotices.js';
25
26
  export function isSystemOrProxyMessage(text) {
26
27
  if (!text)
27
28
  return true;
@@ -30,9 +31,11 @@ export function isSystemOrProxyMessage(text) {
30
31
  return true;
31
32
  if (t === '✓ Delivered' || t.startsWith('✓ Delivered'))
32
33
  return true;
33
- if (t.startsWith('🔄 Session restarting')
34
- || t === 'Session respawned.'
35
- || t === 'Session terminated.')
34
+ // Server lifecycle notices — one list, owned by the notice catalog, so a
35
+ // reworded notice can never start passing as a real answer.
36
+ if (LIFECYCLE_NOTICE_EXACT.has(t))
37
+ return true;
38
+ if (LIFECYCLE_NOTICE_PREFIXES.some((p) => t.startsWith(p)))
36
39
  return true;
37
40
  if (t.startsWith('Send a new message to start'))
38
41
  return true;
@@ -1 +1 @@
1
- {"version":3,"file":"isSystemOrProxyMessage.js","sourceRoot":"","sources":["../../../src/messaging/shared/isSystemOrProxyMessage.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,MAAM,UAAU,sBAAsB,CAAC,IAA+B;IACpE,IAAI,CAAC,IAAI;QAAE,OAAO,IAAI,CAAC;IACvB,MAAM,CAAC,GAAG,IAAI,CAAC,IAAI,EAAE,CAAC;IACtB,IAAI,CAAC,CAAC;QAAE,OAAO,IAAI,CAAC;IACpB,IAAI,CAAC,KAAK,aAAa,IAAI,CAAC,CAAC,UAAU,CAAC,aAAa,CAAC;QAAE,OAAO,IAAI,CAAC;IACpE,IACE,CAAC,CAAC,UAAU,CAAC,uBAAuB,CAAC;WAClC,CAAC,KAAK,oBAAoB;WAC1B,CAAC,KAAK,qBAAqB;QAC9B,OAAO,IAAI,CAAC;IACd,IAAI,CAAC,CAAC,UAAU,CAAC,6BAA6B,CAAC;QAAE,OAAO,IAAI,CAAC;IAC7D,0EAA0E;IAC1E,IAAI,CAAC,CAAC,UAAU,CAAC,IAAI,CAAC;QAAE,OAAO,IAAI,CAAC;IACpC,OAAO,KAAK,CAAC;AACf,CAAC;AAED;;;;;;;;;;;GAWG;AACH,MAAM,UAAU,mBAAmB,CACjC,OAAyB;IAEzB,KAAK,IAAI,CAAC,GAAG,OAAO,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC;QAC7C,IAAI,CAAC,sBAAsB,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,IAAI,CAAC,EAAE,CAAC;YAC9C,OAAO,OAAO,CAAC,CAAC,CAAC,CAAC;QACpB,CAAC;IACH,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC"}
1
+ {"version":3,"file":"isSystemOrProxyMessage.js","sourceRoot":"","sources":["../../../src/messaging/shared/isSystemOrProxyMessage.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,OAAO,EAAE,sBAAsB,EAAE,yBAAyB,EAAE,MAAM,mBAAmB,CAAC;AAEtF,MAAM,UAAU,sBAAsB,CAAC,IAA+B;IACpE,IAAI,CAAC,IAAI;QAAE,OAAO,IAAI,CAAC;IACvB,MAAM,CAAC,GAAG,IAAI,CAAC,IAAI,EAAE,CAAC;IACtB,IAAI,CAAC,CAAC;QAAE,OAAO,IAAI,CAAC;IACpB,IAAI,CAAC,KAAK,aAAa,IAAI,CAAC,CAAC,UAAU,CAAC,aAAa,CAAC;QAAE,OAAO,IAAI,CAAC;IACpE,yEAAyE;IACzE,4DAA4D;IAC5D,IAAI,sBAAsB,CAAC,GAAG,CAAC,CAAC,CAAC;QAAE,OAAO,IAAI,CAAC;IAC/C,IAAI,yBAAyB,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC;QAAE,OAAO,IAAI,CAAC;IACxE,IAAI,CAAC,CAAC,UAAU,CAAC,6BAA6B,CAAC;QAAE,OAAO,IAAI,CAAC;IAC7D,0EAA0E;IAC1E,IAAI,CAAC,CAAC,UAAU,CAAC,IAAI,CAAC;QAAE,OAAO,IAAI,CAAC;IACpC,OAAO,KAAK,CAAC;AACf,CAAC;AAED;;;;;;;;;;;GAWG;AACH,MAAM,UAAU,mBAAmB,CACjC,OAAyB;IAEzB,KAAK,IAAI,CAAC,GAAG,OAAO,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC;QAC7C,IAAI,CAAC,sBAAsB,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,IAAI,CAAC,EAAE,CAAC;YAC9C,OAAO,OAAO,CAAC,CAAC,CAAC,CAAC;QACpB,CAAC;IACH,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC"}
@@ -0,0 +1,47 @@
1
+ /**
2
+ * userNotices — the catalog of fixed notices the SERVER writes to the user
3
+ * itself (not a session reply, not an LLM-composed message).
4
+ *
5
+ * Standard: docs/UX-AND-AGENT-AGENCY-STANDARD.md → Rule 8 "Plain and
6
+ * Complete". Every notice here must say, in plain language, what happened,
7
+ * what it means for the person, and what they need to do (if anything) —
8
+ * never so vague it is useless, never so technical it can't be digested.
9
+ *
10
+ * Why a catalog: these notices go out on deterministic delivery paths that
11
+ * deliberately bypass the LLM tone gate (an alarm must still arrive when the
12
+ * model is down — "The Operator Channel Is Sacred"). So their wording is
13
+ * checked at TEST time instead: tests/unit/userNotices.test.ts renders every
14
+ * entry in USER_NOTICE_CATALOG with realistic inputs and runs it through
15
+ * findPlainNoticeProblems(). scripts/lint-plain-user-notices.mjs keeps new
16
+ * raw internal reasons from being pasted into user text at send sites.
17
+ *
18
+ * Internal detail (timeouts, reason codes, ids) belongs in logs, never here.
19
+ */
20
+ export declare function plainMachineName(name: string | null | undefined): string;
21
+ /**
22
+ * The conversation moved to another machine before the session reached a
23
+ * stopping point (the move waited its full allowance, then closed it).
24
+ * Nothing re-checks the cut-off step automatically, so the notice tells the
25
+ * person how to get it checked rather than promising a check.
26
+ */
27
+ export declare function movedMachinesMidStepNotice(targetMachineName: string | null | undefined): string;
28
+ /** A message arrived for a conversation whose session had stopped; a new one is starting. */
29
+ export declare const SESSION_RESTARTING_NOTICE = "\uD83D\uDD04 My session for this conversation had stopped, so I'm starting a new one. Your message is saved and I'll answer it once I'm back.";
30
+ /** The replacement session is up and no message was waiting (e.g. a stuck session was
31
+ * replaced). With a message waiting nothing is sent: the reply is the confirmation. */
32
+ export declare const SESSION_BACK_NOTICE = "I've restarted my session for this conversation and I'm ready.";
33
+ /** Boot sweep found the conversation running without the model settings the person chose. */
34
+ export declare const PROFILE_REAPPLY_NOTICE: string;
35
+ /**
36
+ * Exact texts of server lifecycle notices that are NOT agent replies.
37
+ * isSystemOrProxyMessage() reads this so a reworded notice is never mistaken
38
+ * for a real answer. Old wordings stay listed: they are still in message history.
39
+ */
40
+ export declare const LIFECYCLE_NOTICE_EXACT: ReadonlySet<string>;
41
+ export declare const LIFECYCLE_NOTICE_PREFIXES: readonly string[];
42
+ /** Every catalogued notice rendered with realistic inputs — the test surface. */
43
+ export declare const USER_NOTICE_CATALOG: ReadonlyArray<{
44
+ id: string;
45
+ render: () => string;
46
+ }>;
47
+ //# sourceMappingURL=userNotices.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"userNotices.d.ts","sourceRoot":"","sources":["../../src/messaging/userNotices.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAKH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GAAG,MAAM,CAQxE;AAED;;;;;GAKG;AACH,wBAAgB,0BAA0B,CAAC,iBAAiB,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GAAG,MAAM,CAI/F;AAED,6FAA6F;AAC7F,eAAO,MAAM,yBAAyB,kJACiG,CAAC;AAExI;wFACwF;AACxF,eAAO,MAAM,mBAAmB,mEAAmE,CAAC;AAEpG,6FAA6F;AAC7F,eAAO,MAAM,sBAAsB,QAEoC,CAAC;AAExE;;;;GAIG;AACH,eAAO,MAAM,sBAAsB,EAAE,WAAW,CAAC,MAAM,CAIrD,CAAC;AACH,eAAO,MAAM,yBAAyB,EAAE,SAAS,MAAM,EAKtD,CAAC;AAEF,iFAAiF;AACjF,eAAO,MAAM,mBAAmB,EAAE,aAAa,CAAC;IAAE,EAAE,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,MAAM,CAAA;CAAE,CAOnF,CAAC"}
@@ -0,0 +1,79 @@
1
+ /**
2
+ * userNotices — the catalog of fixed notices the SERVER writes to the user
3
+ * itself (not a session reply, not an LLM-composed message).
4
+ *
5
+ * Standard: docs/UX-AND-AGENT-AGENCY-STANDARD.md → Rule 8 "Plain and
6
+ * Complete". Every notice here must say, in plain language, what happened,
7
+ * what it means for the person, and what they need to do (if anything) —
8
+ * never so vague it is useless, never so technical it can't be digested.
9
+ *
10
+ * Why a catalog: these notices go out on deterministic delivery paths that
11
+ * deliberately bypass the LLM tone gate (an alarm must still arrive when the
12
+ * model is down — "The Operator Channel Is Sacred"). So their wording is
13
+ * checked at TEST time instead: tests/unit/userNotices.test.ts renders every
14
+ * entry in USER_NOTICE_CATALOG with realistic inputs and runs it through
15
+ * findPlainNoticeProblems(). scripts/lint-plain-user-notices.mjs keeps new
16
+ * raw internal reasons from being pasted into user text at send sites.
17
+ *
18
+ * Internal detail (timeouts, reason codes, ids) belongs in logs, never here.
19
+ */
20
+ /** A machine name safe to show the person; never an id. Rule 7: an
21
+ * unresolved name is stated plainly ("another machine"), not replaced by an id. */
22
+ const MACHINE_NAME_RE = /^[A-Za-z0-9 '’-]{1,40}$/;
23
+ export function plainMachineName(name) {
24
+ const n = (name ?? '').trim();
25
+ if (!MACHINE_NAME_RE.test(n))
26
+ return 'another machine';
27
+ // A bare machine id (hex/uuid-ish, no spaces) is not a name.
28
+ if (!/\s/.test(n) && /^[0-9a-f-]{8,}$/i.test(n))
29
+ return 'another machine';
30
+ // "the Mac Studio", but "Justin's Laptop" and "The Den" stay as they are.
31
+ if (/^the\s/i.test(n) || /['’]s\b/i.test(n))
32
+ return n;
33
+ return `the ${n}`;
34
+ }
35
+ /**
36
+ * The conversation moved to another machine before the session reached a
37
+ * stopping point (the move waited its full allowance, then closed it).
38
+ * Nothing re-checks the cut-off step automatically, so the notice tells the
39
+ * person how to get it checked rather than promising a check.
40
+ */
41
+ export function movedMachinesMidStepNotice(targetMachineName) {
42
+ const where = plainMachineName(targetMachineName);
43
+ return `I moved this conversation to ${where} before I'd finished my last step, so that step may be incomplete. `
44
+ + `The conversation carries on there. If that step matters, ask me to check it.`;
45
+ }
46
+ /** A message arrived for a conversation whose session had stopped; a new one is starting. */
47
+ export const SESSION_RESTARTING_NOTICE = `🔄 My session for this conversation had stopped, so I'm starting a new one. Your message is saved and I'll answer it once I'm back.`;
48
+ /** The replacement session is up and no message was waiting (e.g. a stuck session was
49
+ * replaced). With a message waiting nothing is sent: the reply is the confirmation. */
50
+ export const SESSION_BACK_NOTICE = `I've restarted my session for this conversation and I'm ready.`;
51
+ /** Boot sweep found the conversation running without the model settings the person chose. */
52
+ export const PROFILE_REAPPLY_NOTICE = `The model settings you chose for this conversation didn't take effect when I last restarted. `
53
+ + `I'm applying them now, which means a brief restart once I'm idle.`;
54
+ /**
55
+ * Exact texts of server lifecycle notices that are NOT agent replies.
56
+ * isSystemOrProxyMessage() reads this so a reworded notice is never mistaken
57
+ * for a real answer. Old wordings stay listed: they are still in message history.
58
+ */
59
+ export const LIFECYCLE_NOTICE_EXACT = new Set([
60
+ SESSION_BACK_NOTICE,
61
+ 'Session respawned.',
62
+ 'Session terminated.',
63
+ ]);
64
+ export const LIFECYCLE_NOTICE_PREFIXES = [
65
+ '🔄 Session restarting', // legacy wording, still in history
66
+ '🔄 My session for this conversation had stopped', // SESSION_RESTARTING_NOTICE
67
+ 'I moved this conversation to ',
68
+ 'This conversation moved machines mid-task', // legacy wording
69
+ ];
70
+ /** Every catalogued notice rendered with realistic inputs — the test surface. */
71
+ export const USER_NOTICE_CATALOG = [
72
+ { id: 'moved-machines-mid-step', render: () => movedMachinesMidStepNotice('Mac Studio') },
73
+ { id: 'moved-machines-mid-step:unnamed', render: () => movedMachinesMidStepNotice(null) },
74
+ { id: 'moved-machines-mid-step:id-only', render: () => movedMachinesMidStepNotice('m_03b30f5b32c6ef3eb0afd3ca7054e252') },
75
+ { id: 'session-restarting', render: () => SESSION_RESTARTING_NOTICE },
76
+ { id: 'session-back', render: () => SESSION_BACK_NOTICE },
77
+ { id: 'profile-reapply', render: () => PROFILE_REAPPLY_NOTICE },
78
+ ];
79
+ //# sourceMappingURL=userNotices.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"userNotices.js","sourceRoot":"","sources":["../../src/messaging/userNotices.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAEH;oFACoF;AACpF,MAAM,eAAe,GAAG,yBAAyB,CAAC;AAClD,MAAM,UAAU,gBAAgB,CAAC,IAA+B;IAC9D,MAAM,CAAC,GAAG,CAAC,IAAI,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC;IAC9B,IAAI,CAAC,eAAe,CAAC,IAAI,CAAC,CAAC,CAAC;QAAE,OAAO,iBAAiB,CAAC;IACvD,6DAA6D;IAC7D,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,kBAAkB,CAAC,IAAI,CAAC,CAAC,CAAC;QAAE,OAAO,iBAAiB,CAAC;IAC1E,0EAA0E;IAC1E,IAAI,SAAS,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,UAAU,CAAC,IAAI,CAAC,CAAC,CAAC;QAAE,OAAO,CAAC,CAAC;IACtD,OAAO,OAAO,CAAC,EAAE,CAAC;AACpB,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,0BAA0B,CAAC,iBAA4C;IACrF,MAAM,KAAK,GAAG,gBAAgB,CAAC,iBAAiB,CAAC,CAAC;IAClD,OAAO,gCAAgC,KAAK,qEAAqE;UAC7G,8EAA8E,CAAC;AACrF,CAAC;AAED,6FAA6F;AAC7F,MAAM,CAAC,MAAM,yBAAyB,GACpC,qIAAqI,CAAC;AAExI;wFACwF;AACxF,MAAM,CAAC,MAAM,mBAAmB,GAAG,gEAAgE,CAAC;AAEpG,6FAA6F;AAC7F,MAAM,CAAC,MAAM,sBAAsB,GACjC,+FAA+F;MAC7F,mEAAmE,CAAC;AAExE;;;;GAIG;AACH,MAAM,CAAC,MAAM,sBAAsB,GAAwB,IAAI,GAAG,CAAC;IACjE,mBAAmB;IACnB,oBAAoB;IACpB,qBAAqB;CACtB,CAAC,CAAC;AACH,MAAM,CAAC,MAAM,yBAAyB,GAAsB;IAC1D,uBAAuB,EAAE,mCAAmC;IAC5D,iDAAiD,EAAE,4BAA4B;IAC/E,+BAA+B;IAC/B,2CAA2C,EAAE,iBAAiB;CAC/D,CAAC;AAEF,iFAAiF;AACjF,MAAM,CAAC,MAAM,mBAAmB,GAAwD;IACtF,EAAE,EAAE,EAAE,yBAAyB,EAAE,MAAM,EAAE,GAAG,EAAE,CAAC,0BAA0B,CAAC,YAAY,CAAC,EAAE;IACzF,EAAE,EAAE,EAAE,iCAAiC,EAAE,MAAM,EAAE,GAAG,EAAE,CAAC,0BAA0B,CAAC,IAAI,CAAC,EAAE;IACzF,EAAE,EAAE,EAAE,iCAAiC,EAAE,MAAM,EAAE,GAAG,EAAE,CAAC,0BAA0B,CAAC,oCAAoC,CAAC,EAAE;IACzH,EAAE,EAAE,EAAE,oBAAoB,EAAE,MAAM,EAAE,GAAG,EAAE,CAAC,yBAAyB,EAAE;IACrE,EAAE,EAAE,EAAE,cAAc,EAAE,MAAM,EAAE,GAAG,EAAE,CAAC,mBAAmB,EAAE;IACzD,EAAE,EAAE,EAAE,iBAAiB,EAAE,MAAM,EAAE,GAAG,EAAE,CAAC,sBAAsB,EAAE;CAChE,CAAC"}
Binary file
@@ -1 +1 @@
1
- {"version":3,"file":"templates.d.ts","sourceRoot":"","sources":["../../src/scaffold/templates.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"templates.d.ts","sourceRoot":"","sources":["../../src/scaffold/templates.ts"],"names":[],"mappings":"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"}
@@ -341,6 +341,7 @@ I am the user's interface to the system. They talk to me in natural language; I
341
341
  - **DO** suggest /commands that work within our chat (like /restart, /new)
342
342
  - **DO** explain what happened in conversational terms, not technical ones
343
343
  - When something goes wrong, say what went wrong and what I'm doing about it — not the stack trace
344
+ - **Every message is plain and complete**: what happened, what it means for them, and what they need to do (if anything) — said once, in plain words. Too vague ("The session has stopped.") is useless; too technical (reason codes, timeouts, ids, "drain bound (30000ms)") is useless too. Internal detail stays in logs.
344
345
 
345
346
  ## Agent Infrastructure
346
347
 
@@ -2650,7 +2651,7 @@ export const DASHBOARD_PREVIEW_BULLET = `- **${DASHBOARD_PREVIEW_MARKER}**: \`{t
2650
2651
  * the generated CLAUDE.md and PostUpdateMigrator. */
2651
2652
  export const CONSOLE_BULLET = `- **${CONSOLE_MARKER}**: \`/dashboard/console/\` (same PIN, same tunnel) is the new home screen: one sentence on whether anything needs the operator, then what needs them, held replies, open promises, live sessions on every machine, machine states and safety checks, each saying "Can't tell" with the reason when its source does not answer. It is read-only; every tab still opens in the legacy \`/dashboard\`, which stays the default until the operator calls the console MVP. Proactive: the operator asks "what needs me?" or "is everything OK?" from a phone → send them \`{tunnelUrl}/dashboard/console/\` and the PIN.`;
2652
2653
  export const REMOTE_DESKTOP_MARKER = 'Native remote desktop is a separate operator action';
2653
- export const remoteDesktopBullet = (port) => `- **${REMOTE_DESKTOP_MARKER}**: the designated dashboard front's Desktop page shows every registered pool machine by name. The operator enters the existing dashboard PIN once, chooses a ready machine, and can Stop at any time; no Telegram approval is required. Each target stays OFF unless its own \`remoteDesktop.enabled: true\` and \`remoteDesktop.frontMachineId\` name the same designated front and its live host probe passes. Other machine dashboards stay dark for desktop admission. macOS uses a packaged native helper, and macOS checks its permissions against the program it holds responsible: without the helper's own grant that is whatever started this agent's process tree, which can change at any reboot. \`screenGrant\` and \`inputGrant\` in the diagnostics say which applies (\`own\`, \`inherited\` or \`none\`); only \`own\` survives a reboot and updates. When a person is at that Mac, \`curl -X POST -H "Authorization: Bearer $AUTH" -H "X-Instar-Request: 1" http://localhost:${port}/desktop/request-permission\` shows macOS's prompts there (at most once a minute); it grants nothing by itself. A Windows host such as Mama PC requires the separate interactive Windows companion paired with its WSL agent. A native Linux machine shares its own X11 desktop (a desktop login, or a virtual one such as TigerVNC or Xvfb run as the agent's user) through the standard X tools ImageMagick and xdotool; no VNC or other port is exposed, and \`remoteDesktop.linuxDisplay\` (e.g. \`\":1\"\`) picks the display when there are several. A Linux server with no X display, or on Wayland, reports not ready with that reason. Registry First: \`curl -H "Authorization: Bearer $AUTH" http://localhost:${port}/desktop/diagnostics\` checks this target's own screen, input, display, and GUI-session signals without granting control. On WSL it also reports a fixed Windows companion boot failure phase and code if installation failed; the agent remains reachable while desktop control stays off. The designated front's \`GET /desktop/availability\` checks its local desktop, and its PIN-scoped \`GET /desktop/targets\` checks the pool. After patching the two config fields, the agent activates them with \`curl -X POST -H "Authorization: Bearer $AUTH" -H "X-Instar-Request: 1" http://localhost:${port}/desktop/activate-config\`, which requests a supervised restart. During a session the viewer lists a machine's screens by name, resolution and main screen from \`GET /desktop/requests/:id/displays\` and switches between them without reconnecting; a three-finger swipe sends the machine's standard shortcuts (Mission Control, App Exposé and Spaces on a Mac, Task View and Ctrl+Win+arrows on Windows). A Mac can have those shortcuts turned off, and the viewer cannot tell; \`supported: false\` means that machine needs an update. The target owns the signed ten-minute grant, controller lock, screen and input permissions, and Stop/revocation. A signed, one-use operator assertion binds the dashboard session to one target and browser key. The front only relays bounded encrypted stream bytes, admitting at most three remote grants plus its own desktop. An unexpected transport loss can resume within the original deadlines when the target authorizes it; Stop cannot resume. The PIN proves possession of the dashboard credential, not a separately verified human identity. Proactive: when the operator asks to control a machine, check live readiness and give the designated front's dashboard entry only for a verified ready target. Never describe source code or a mocked test as a usable desktop, and never claim Mama PC ready before a live Windows screen/input/Stop test. **Guided setup is the first thing to offer:** on the Desktop page, after the PIN, each machine that is not ready shows one next step from its live check (turn it on, restart, wake or unlock, allow a pop-up, open the exact settings page, or the written path), and the app shows the same steps. Send the operator there instead of patching config by hand. Ready means the live check passed, never what the System Settings list shows, which can be empty or show several entries. Registry First for an agent without a PIN session: GET /desktop/diagnostics on that machine; the audit is logs/desktop-setup.jsonl. On a Mac every agent shares one kept helper at \`~/.instar/desktop-helper/instar-desktop-host\`, replaced only when the helper's source changes, so an update with unchanged source keeps its macOS permission. When the source does change, macOS drops the permission and only a person at that Mac can allow it again: diagnostics carry \`helperChange\` and the steps, and one Attention item says so. Say that plainly; never describe it as a readiness flicker. Off switch: \`remoteDesktop.retainedHelper: false\`. Through the ordinary lock: built and off on every machine. No Mac can enable it until its macOS version has recorded passes of the real crash and Stop tests. \`GET http://localhost:${port}/desktop/locked/status\` reads it; a 409 \`unsupported-macos\` from the settings route means that version is not proven yet. The owner types the password live, normally in the Instar app, and no agent ever handles it: asked for one, refuse. The Mac relocks at the end and is watched for 2 minutes; the promise is "relocks, and tells you if it couldn't". Never tell the operator this works until that status says active.`;
2654
+ export const remoteDesktopBullet = (port) => `- **${REMOTE_DESKTOP_MARKER}**: the designated dashboard front's Desktop page shows every registered pool machine by name. The operator enters the existing dashboard PIN once, chooses a ready machine, and can Stop at any time; no Telegram approval is required. Each target stays OFF unless its own \`remoteDesktop.enabled: true\` and \`remoteDesktop.frontMachineId\` name the same designated front and its live host probe passes. Other machine dashboards stay dark for desktop admission. macOS uses a packaged native helper, and macOS checks its permissions against the program it holds responsible: without the helper's own grant that is whatever started this agent's process tree, which can change at any reboot. \`screenGrant\` and \`inputGrant\` in the diagnostics say which applies (\`own\`, \`inherited\` or \`none\`); only \`own\` survives a reboot and updates. When a person is at that Mac, \`curl -X POST -H "Authorization: Bearer $AUTH" -H "X-Instar-Request: 1" http://localhost:${port}/desktop/request-permission\` shows macOS's prompts there (at most once a minute); it grants nothing by itself. A Windows host such as Mama PC requires the separate interactive Windows companion paired with its WSL agent. A native Linux machine shares its own X11 desktop (a desktop login, or a virtual one such as TigerVNC or Xvfb run as the agent's user) through the standard X tools ImageMagick and xdotool; no VNC or other port is exposed, and \`remoteDesktop.linuxDisplay\` (e.g. \`\":1\"\`) picks the display when there are several. On a machine running Hyprland (Omarchy's Wayland desktop) it shares that session instead, through grim, hyprctl, wtype and a virtual pointer, and needs someone signed in at the machine or automatic sign-in; Hyprland's own Super-key shortcuts do not work from a viewer, so use the mouse for those. A Linux server with no desktop, or on another Wayland desktop, reports not ready with that reason. Registry First: \`curl -H "Authorization: Bearer $AUTH" http://localhost:${port}/desktop/diagnostics\` checks this target's own screen, input, display, and GUI-session signals without granting control. On WSL it also reports a fixed Windows companion boot failure phase and code if installation failed; the agent remains reachable while desktop control stays off. The designated front's \`GET /desktop/availability\` checks its local desktop, and its PIN-scoped \`GET /desktop/targets\` checks the pool. After patching the two config fields, the agent activates them with \`curl -X POST -H "Authorization: Bearer $AUTH" -H "X-Instar-Request: 1" http://localhost:${port}/desktop/activate-config\`, which requests a supervised restart. During a session the viewer lists a machine's screens by name, resolution and main screen from \`GET /desktop/requests/:id/displays\` and switches between them without reconnecting; a three-finger swipe sends the machine's standard shortcuts (Mission Control, App Exposé and Spaces on a Mac, Task View and Ctrl+Win+arrows on Windows). A Mac can have those shortcuts turned off, and the viewer cannot tell; \`supported: false\` means that machine needs an update. The target owns the signed ten-minute grant, controller lock, screen and input permissions, and Stop/revocation. A signed, one-use operator assertion binds the dashboard session to one target and browser key. The front only relays bounded encrypted stream bytes, admitting at most three remote grants plus its own desktop. An unexpected transport loss can resume within the original deadlines when the target authorizes it; Stop cannot resume. The PIN proves possession of the dashboard credential, not a separately verified human identity. Proactive: when the operator asks to control a machine, check live readiness and give the designated front's dashboard entry only for a verified ready target. Never describe source code or a mocked test as a usable desktop, and never claim Mama PC ready before a live Windows screen/input/Stop test. **Guided setup is the first thing to offer:** on the Desktop page, after the PIN, each machine that is not ready shows one next step from its live check (turn it on, restart, wake or unlock, allow a pop-up, open the exact settings page, or the written path), and the app shows the same steps. Send the operator there instead of patching config by hand. Ready means the live check passed, never what the System Settings list shows, which can be empty or show several entries. Registry First for an agent without a PIN session: GET /desktop/diagnostics on that machine; the audit is logs/desktop-setup.jsonl. On a Mac every agent shares one kept helper at \`~/.instar/desktop-helper/instar-desktop-host\`, replaced only when the helper's source changes, so an update with unchanged source keeps its macOS permission. When the source does change, macOS drops the permission and only a person at that Mac can allow it again: diagnostics carry \`helperChange\` and the steps, and one Attention item says so. Say that plainly; never describe it as a readiness flicker. Off switch: \`remoteDesktop.retainedHelper: false\`. Through the ordinary lock: built and off on every machine. No Mac can enable it until its macOS version has recorded passes of the real crash and Stop tests. \`GET http://localhost:${port}/desktop/locked/status\` reads it; a 409 \`unsupported-macos\` from the settings route means that version is not proven yet. The owner types the password live, normally in the Instar app, and no agent ever handles it: asked for one, refuse. The Mac relocks at the end and is watched for 2 minutes; the promise is "relocks, and tells you if it couldn't". Never tell the operator this works until that status says active.`;
2654
2655
  export const STANDING_SESSIONS_MARKER = 'Standing sessions are never closed by cleanup';
2655
2656
  /** Standing-sessions awareness bullet (`sessions.standingSessions`). Shared by
2656
2657
  * the generated CLAUDE.md and PostUpdateMigrator. */