aegis-desktop 1.0.13 → 1.0.15
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/SOURCE-OFFER.txt +9 -0
- package/bin/aegis.js +1 -32
- package/lib/account-key.js +1 -56
- package/lib/auto.js +1 -430
- package/lib/avatar/companion.js +1 -698
- package/lib/avatar/cosmetics.js +1 -532
- package/lib/avatar/events.js +1 -344
- package/lib/avatar/holder-turn.js +1 -350
- package/lib/avatar/holders.js +1 -612
- package/lib/avatar/identity.js +1 -641
- package/lib/avatar/level.js +1 -368
- package/lib/avatar/mirror.js +1 -524
- package/lib/avatar/persona.js +1 -388
- package/lib/avatar/profile.js +1 -977
- package/lib/avatar/register.js +1 -307
- package/lib/avatar/store.js +1 -472
- package/lib/avatar/tiers.js +1 -38
- package/lib/avatar/transfer.js +1 -415
- package/lib/avatar/turn.js +1 -216
- package/lib/avatar/voice.js +1 -328
- package/lib/avatar/xp.js +1 -404
- package/lib/deep-link.js +1 -120
- package/lib/fingerprint.js +2 -347
- package/lib/google-connect.js +1 -169
- package/lib/local/agents.js +1 -105
- package/lib/local/autonomous.js +1 -1045
- package/lib/local/claude-pro.js +1 -993
- package/lib/local/context.js +1 -95
- package/lib/local/engine.js +1 -3228
- package/lib/local/entitlement.js +1 -654
- package/lib/local/git-scope.js +1 -228
- package/lib/local/local.js +1 -2135
- package/lib/local/prompt.js +1 -275
- package/lib/local/queue.js +1 -560
- package/lib/local/session-rounds.js +1 -208
- package/lib/local/shell.js +1 -680
- package/lib/local/telemetry.js +1 -470
- package/lib/local/tools.js +1 -1238
- package/lib/local/turn-guard.js +1 -300
- package/lib/local/worktree-lock.js +1 -235
- package/lib/provider-key-mirror.js +1 -252
- package/lib/quick-launcher.js +1 -84
- package/lib/settings.js +1 -890
- package/lib/sync/memory-queue.js +1 -60
- package/lib/sync/persist-gate.js +1 -148
- package/lib/sync/sessions.js +1 -106
- package/lib/window-state.js +1 -66
- package/main.js +3 -4390
- package/package.json +3 -5
- package/preload.js +3 -628
- package/renderer/app.js +1 -6560
- package/renderer/avatar/assemble.js +1 -335
- package/renderer/avatar/avatar.js +1 -380
- package/renderer/avatar/cards.js +1 -141
- package/renderer/avatar/hud.js +1 -206
- package/renderer/avatar/machine.js +1 -213
- package/renderer/avatar/pane.js +1 -439
- package/renderer/avatar/parts.js +1 -437
- package/renderer/budget.js +1 -168
- package/renderer/companion.js +1 -223
- package/renderer/diff.js +1 -359
- package/renderer/diffview.js +1 -336
- package/renderer/fingerprint.js +3 -720
- package/renderer/markdown.js +1 -168
- package/renderer/provider-toolbar.js +1 -134
- package/renderer/providers.js +1 -259
- package/renderer/quick.js +1 -191
- package/renderer/stream-policy.js +1 -306
- package/renderer/transcript-view.js +1 -323
- package/renderer/usage.js +1 -1239
- package/vendor/aegis.js +1 -2054
- package/vendor/attest.js +1 -442
- package/vendor/authorship-key.js +1 -410
- package/vendor/auto-provision.js +1 -134
- package/vendor/brain-catalog.js +1 -268
- package/vendor/byok-catalog.js +1 -230
- package/vendor/credentials.js +1 -494
- package/vendor/device-fingerprint.js +1 -141
- package/vendor/drive-connect.js +1 -85
- package/vendor/drive-upload.js +1 -174
- package/vendor/env-file.js +1 -1273
- package/vendor/fingerprint.js +1 -1064
- package/vendor/foreign-memory.js +1 -669
- package/vendor/google-signin.js +1 -125
- package/vendor/hooks.js +1 -430
- package/vendor/model-discovery.js +1 -330
- package/vendor/platform.js +1 -937
- package/vendor/prefs.js +1 -351
- package/vendor/presence-constants.js +1 -107
- package/vendor/presence.js +1 -438
- package/vendor/record-vault.js +3 -1055
- package/vendor/session-store.js +1 -548
- package/vendor/topup.js +1 -297
- package/vendor/touch.js +1 -458
- package/vendor/update.js +1 -704
- package/vendor/user-models.js +1 -381
package/lib/avatar/events.js
CHANGED
|
@@ -1,346 +1,3 @@
|
|
|
1
1
|
// SPDX-License-Identifier: AGPL-3.0-or-later
|
|
2
2
|
// Copyright (C) 2026 Niklas Borneklint — AEGIS Code (aegiscode) / Aegiscloud.
|
|
3
|
-
|
|
4
|
-
// Conveying a modified copy, or running one as a network service, owes the
|
|
5
|
-
// corresponding source under this licence (§5/§13) — see LICENSING.md.
|
|
6
|
-
'use strict';
|
|
7
|
-
|
|
8
|
-
/**
|
|
9
|
-
* events.js — the avatar's state machine, driven by real engine signals.
|
|
10
|
-
*
|
|
11
|
-
* Two decisions shape this file:
|
|
12
|
-
*
|
|
13
|
-
* 1. NO TIMERS INSIDE. A machine that owns a `setTimeout` is a machine that
|
|
14
|
-
* leaks one, keeps the Electron process alive at quit, and cannot be tested
|
|
15
|
-
* without sleeping. The caller passes `tick(now)` — main already has a clock
|
|
16
|
-
* and the renderer already runs a frame loop — so idle-timeout behaviour is
|
|
17
|
-
* deterministic and instant in tests.
|
|
18
|
-
*
|
|
19
|
-
* 2. UNKNOWN SIGNALS ARE NO-OPS, NEVER THROWS. The signals come from turn
|
|
20
|
-
* assembly, the approval card, the queue progress renderer and the sync
|
|
21
|
-
* status line — four places that will grow new events over time. A machine
|
|
22
|
-
* that throws on an unrecognised signal turns every future feature into a
|
|
23
|
-
* crash in the avatar, so the default transition is "stay put".
|
|
24
|
-
*
|
|
25
|
-
* The states are wired to signals that
|
|
26
|
-
* already exist: turn start/stream/end, `tool.*` from the tool host, the
|
|
27
|
-
* approval card, queue progress, and sync status. No new event bus.
|
|
28
|
-
*
|
|
29
|
-
* Pure: no DOM, no Electron, no fs.
|
|
30
|
-
*/
|
|
31
|
-
|
|
32
|
-
const STATES = Object.freeze([
|
|
33
|
-
'idle',
|
|
34
|
-
'listening',
|
|
35
|
-
'thinking',
|
|
36
|
-
'reading',
|
|
37
|
-
'writing',
|
|
38
|
-
'running',
|
|
39
|
-
'awaiting-you',
|
|
40
|
-
'celebrating',
|
|
41
|
-
'dozing',
|
|
42
|
-
'error',
|
|
43
|
-
]);
|
|
44
|
-
|
|
45
|
-
const SIGNALS = Object.freeze([
|
|
46
|
-
'turn.start',
|
|
47
|
-
'turn.stream',
|
|
48
|
-
'turn.end',
|
|
49
|
-
'tool.read',
|
|
50
|
-
'tool.write',
|
|
51
|
-
'tool.run',
|
|
52
|
-
'approval.card',
|
|
53
|
-
'approval.decided',
|
|
54
|
-
'queue.progress',
|
|
55
|
-
'sync.error',
|
|
56
|
-
'sync.ok',
|
|
57
|
-
'celebrate.done',
|
|
58
|
-
'poke',
|
|
59
|
-
'reset',
|
|
60
|
-
'idle.timeout',
|
|
61
|
-
]);
|
|
62
|
-
|
|
63
|
-
/** Signals that mean "the user or the engine is doing something". */
|
|
64
|
-
const ACTIVITY_SIGNALS = Object.freeze([
|
|
65
|
-
'turn.start',
|
|
66
|
-
'turn.stream',
|
|
67
|
-
'tool.read',
|
|
68
|
-
'tool.write',
|
|
69
|
-
'tool.run',
|
|
70
|
-
'approval.card',
|
|
71
|
-
'approval.decided',
|
|
72
|
-
'queue.progress',
|
|
73
|
-
'poke',
|
|
74
|
-
]);
|
|
75
|
-
|
|
76
|
-
const DEFAULT_IDLE_TIMEOUT_MS = 5 * 60 * 1000;
|
|
77
|
-
|
|
78
|
-
/** turn.end celebrates only when the turn actually produced something green. */
|
|
79
|
-
function afterTurnEnd(meta) {
|
|
80
|
-
return meta && meta.green ? 'celebrating' : 'idle';
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
/**
|
|
84
|
-
* Transition table: state → signal → next state.
|
|
85
|
-
*
|
|
86
|
-
* A value may be a state name or a `(meta) => state` function. A missing entry
|
|
87
|
-
* means no transition (state unchanged, `changed: false`).
|
|
88
|
-
*
|
|
89
|
-
* `sync.error` is listed on every non-error state rather than special-cased in
|
|
90
|
-
* code, so reading the table tells you the whole machine.
|
|
91
|
-
*/
|
|
92
|
-
const TABLE = Object.freeze({
|
|
93
|
-
idle: {
|
|
94
|
-
'turn.start': 'listening',
|
|
95
|
-
'approval.card': 'awaiting-you',
|
|
96
|
-
'queue.progress': 'running',
|
|
97
|
-
'idle.timeout': 'dozing',
|
|
98
|
-
'sync.error': 'error',
|
|
99
|
-
reset: 'idle',
|
|
100
|
-
},
|
|
101
|
-
listening: {
|
|
102
|
-
'turn.start': 'listening',
|
|
103
|
-
'turn.stream': 'thinking',
|
|
104
|
-
'tool.read': 'reading',
|
|
105
|
-
'tool.write': 'writing',
|
|
106
|
-
'tool.run': 'running',
|
|
107
|
-
'approval.card': 'awaiting-you',
|
|
108
|
-
'turn.end': afterTurnEnd,
|
|
109
|
-
'sync.error': 'error',
|
|
110
|
-
reset: 'idle',
|
|
111
|
-
},
|
|
112
|
-
thinking: {
|
|
113
|
-
'turn.stream': 'thinking',
|
|
114
|
-
'tool.read': 'reading',
|
|
115
|
-
'tool.write': 'writing',
|
|
116
|
-
'tool.run': 'running',
|
|
117
|
-
'approval.card': 'awaiting-you',
|
|
118
|
-
'turn.end': afterTurnEnd,
|
|
119
|
-
'sync.error': 'error',
|
|
120
|
-
reset: 'idle',
|
|
121
|
-
},
|
|
122
|
-
reading: {
|
|
123
|
-
'tool.read': 'reading',
|
|
124
|
-
'tool.write': 'writing',
|
|
125
|
-
'tool.run': 'running',
|
|
126
|
-
'turn.stream': 'thinking',
|
|
127
|
-
'approval.card': 'awaiting-you',
|
|
128
|
-
'turn.end': afterTurnEnd,
|
|
129
|
-
'sync.error': 'error',
|
|
130
|
-
reset: 'idle',
|
|
131
|
-
},
|
|
132
|
-
writing: {
|
|
133
|
-
'tool.write': 'writing',
|
|
134
|
-
'tool.read': 'reading',
|
|
135
|
-
'tool.run': 'running',
|
|
136
|
-
'turn.stream': 'thinking',
|
|
137
|
-
'approval.card': 'awaiting-you',
|
|
138
|
-
'turn.end': afterTurnEnd,
|
|
139
|
-
'sync.error': 'error',
|
|
140
|
-
reset: 'idle',
|
|
141
|
-
},
|
|
142
|
-
running: {
|
|
143
|
-
'tool.run': 'running',
|
|
144
|
-
'tool.read': 'reading',
|
|
145
|
-
'tool.write': 'writing',
|
|
146
|
-
'queue.progress': 'running',
|
|
147
|
-
'turn.stream': 'thinking',
|
|
148
|
-
'approval.card': 'awaiting-you',
|
|
149
|
-
'turn.end': afterTurnEnd,
|
|
150
|
-
'sync.error': 'error',
|
|
151
|
-
reset: 'idle',
|
|
152
|
-
},
|
|
153
|
-
'awaiting-you': {
|
|
154
|
-
'approval.card': 'awaiting-you',
|
|
155
|
-
'approval.decided': 'thinking',
|
|
156
|
-
'turn.end': afterTurnEnd,
|
|
157
|
-
'sync.error': 'error',
|
|
158
|
-
reset: 'idle',
|
|
159
|
-
},
|
|
160
|
-
celebrating: {
|
|
161
|
-
'celebrate.done': 'idle',
|
|
162
|
-
'turn.start': 'listening',
|
|
163
|
-
'sync.error': 'error',
|
|
164
|
-
reset: 'idle',
|
|
165
|
-
},
|
|
166
|
-
dozing: {
|
|
167
|
-
poke: 'idle',
|
|
168
|
-
'turn.start': 'listening',
|
|
169
|
-
'approval.card': 'awaiting-you',
|
|
170
|
-
'sync.error': 'error',
|
|
171
|
-
reset: 'idle',
|
|
172
|
-
},
|
|
173
|
-
error: {
|
|
174
|
-
reset: 'idle',
|
|
175
|
-
'sync.ok': 'idle',
|
|
176
|
-
'turn.start': 'listening',
|
|
177
|
-
'sync.error': 'error',
|
|
178
|
-
},
|
|
179
|
-
});
|
|
180
|
-
|
|
181
|
-
/**
|
|
182
|
-
* Expression id per state. The renderer maps these to the persona's expression
|
|
183
|
-
* pack; keeping the mapping here means the renderer needs no state knowledge.
|
|
184
|
-
*/
|
|
185
|
-
const EXPRESSIONS = Object.freeze({
|
|
186
|
-
idle: 'neutral',
|
|
187
|
-
listening: 'attentive',
|
|
188
|
-
thinking: 'focused',
|
|
189
|
-
reading: 'focused',
|
|
190
|
-
writing: 'focused',
|
|
191
|
-
running: 'focused',
|
|
192
|
-
'awaiting-you': 'waiting',
|
|
193
|
-
celebrating: 'happy',
|
|
194
|
-
dozing: 'sleepy',
|
|
195
|
-
error: 'concerned',
|
|
196
|
-
});
|
|
197
|
-
|
|
198
|
-
/** Is this a state we know how to draw / reason about? */
|
|
199
|
-
function isState(state) {
|
|
200
|
-
return STATES.includes(state);
|
|
201
|
-
}
|
|
202
|
-
|
|
203
|
-
function expressionFor(state) {
|
|
204
|
-
return EXPRESSIONS[state] || 'neutral';
|
|
205
|
-
}
|
|
206
|
-
|
|
207
|
-
/**
|
|
208
|
-
* Build a machine.
|
|
209
|
-
*
|
|
210
|
-
* @param {object} [opts]
|
|
211
|
-
* @param {string} [opts.state] starting state (default 'idle')
|
|
212
|
-
* @param {number} [opts.now] starting clock, ms
|
|
213
|
-
* @param {number} [opts.idleTimeoutMs] silence before `idle` → `dozing`
|
|
214
|
-
* @param {number} [opts.historyLimit] transition log length (default 64)
|
|
215
|
-
*/
|
|
216
|
-
function createMachine(opts) {
|
|
217
|
-
// A default parameter only covers `undefined`; `createMachine(null)` would
|
|
218
|
-
// throw on property access. Construction is total on purpose — main builds
|
|
219
|
-
// this from whatever the config file happened to contain.
|
|
220
|
-
const o = opts && typeof opts === 'object' ? opts : {};
|
|
221
|
-
const idleTimeoutMs = Number.isFinite(o.idleTimeoutMs) && o.idleTimeoutMs > 0
|
|
222
|
-
? o.idleTimeoutMs
|
|
223
|
-
: DEFAULT_IDLE_TIMEOUT_MS;
|
|
224
|
-
let state = isState(o.state) ? o.state : 'idle';
|
|
225
|
-
let clock = Number.isFinite(o.now) ? o.now : 0;
|
|
226
|
-
let lastActivity = clock;
|
|
227
|
-
let tickCount = 0;
|
|
228
|
-
const historyLimit = Number.isFinite(o.historyLimit) && o.historyLimit > 0 ? o.historyLimit : 64;
|
|
229
|
-
const history = [];
|
|
230
|
-
const listeners = new Set();
|
|
231
|
-
|
|
232
|
-
function emit(event) {
|
|
233
|
-
history.push(event);
|
|
234
|
-
if (history.length > historyLimit) history.shift();
|
|
235
|
-
for (const fn of listeners) {
|
|
236
|
-
try {
|
|
237
|
-
fn(event);
|
|
238
|
-
} catch {
|
|
239
|
-
// A subscriber that throws must not take the machine (or the turn) with
|
|
240
|
-
// it — the state is already updated by the time listeners run.
|
|
241
|
-
}
|
|
242
|
-
}
|
|
243
|
-
}
|
|
244
|
-
|
|
245
|
-
/**
|
|
246
|
-
* Send a signal.
|
|
247
|
-
* @param {string} signal
|
|
248
|
-
* @param {object} [meta] e.g. { green: true } on turn.end
|
|
249
|
-
* @returns {{ changed: boolean, state: string, prev: string, signal: string }}
|
|
250
|
-
*/
|
|
251
|
-
function send(signal, meta) {
|
|
252
|
-
const prev = state;
|
|
253
|
-
const row = TABLE[state] || {};
|
|
254
|
-
const target = row[String(signal)];
|
|
255
|
-
let next = state;
|
|
256
|
-
if (typeof target === 'function') next = target(meta || {});
|
|
257
|
-
else if (typeof target === 'string') next = target;
|
|
258
|
-
|
|
259
|
-
if (!isState(next)) next = state;
|
|
260
|
-
const changed = next !== prev;
|
|
261
|
-
if (changed) {
|
|
262
|
-
state = next;
|
|
263
|
-
}
|
|
264
|
-
if (ACTIVITY_SIGNALS.includes(String(signal))) {
|
|
265
|
-
lastActivity = clock;
|
|
266
|
-
}
|
|
267
|
-
emit({ signal: String(signal), prev, state, changed, meta: meta || null, t: clock });
|
|
268
|
-
return { changed, state, prev, signal: String(signal) };
|
|
269
|
-
}
|
|
270
|
-
|
|
271
|
-
/**
|
|
272
|
-
* Advance the clock and let the machine decide whether silence has become
|
|
273
|
-
* `dozing`. Returns the same shape as `send`, with `signal: null` when the
|
|
274
|
-
* clock moved but nothing transitioned — the common case.
|
|
275
|
-
*
|
|
276
|
-
* Deliberately conservative: only `idle` dozes. A long-running turn, an open
|
|
277
|
-
* approval card, or a stream that is thinking must never be interrupted by
|
|
278
|
-
* the avatar falling asleep.
|
|
279
|
-
*/
|
|
280
|
-
function tick(now) {
|
|
281
|
-
if (Number.isFinite(now)) clock = Math.max(clock, now);
|
|
282
|
-
tickCount += 1;
|
|
283
|
-
if (state !== 'idle') return { changed: false, state, prev: state, signal: null };
|
|
284
|
-
if (clock - lastActivity < idleTimeoutMs) return { changed: false, state, prev: state, signal: null };
|
|
285
|
-
return send('idle.timeout');
|
|
286
|
-
}
|
|
287
|
-
|
|
288
|
-
return {
|
|
289
|
-
get state() {
|
|
290
|
-
return state;
|
|
291
|
-
},
|
|
292
|
-
get expression() {
|
|
293
|
-
return expressionFor(state);
|
|
294
|
-
},
|
|
295
|
-
get lastActivity() {
|
|
296
|
-
return lastActivity;
|
|
297
|
-
},
|
|
298
|
-
get ticks() {
|
|
299
|
-
return tickCount;
|
|
300
|
-
},
|
|
301
|
-
history,
|
|
302
|
-
send,
|
|
303
|
-
tick,
|
|
304
|
-
/** Set the clock without evaluating an idle timeout. */
|
|
305
|
-
setNow(now) {
|
|
306
|
-
if (Number.isFinite(now)) clock = Math.max(clock, now);
|
|
307
|
-
return clock;
|
|
308
|
-
},
|
|
309
|
-
on(fn) {
|
|
310
|
-
if (typeof fn !== 'function') throw new TypeError('listener must be a function');
|
|
311
|
-
listeners.add(fn);
|
|
312
|
-
return () => listeners.delete(fn);
|
|
313
|
-
},
|
|
314
|
-
reset(now) {
|
|
315
|
-
listeners.clear();
|
|
316
|
-
history.length = 0;
|
|
317
|
-
state = 'idle';
|
|
318
|
-
lastActivity = Number.isFinite(now) ? now : clock;
|
|
319
|
-
return state;
|
|
320
|
-
},
|
|
321
|
-
};
|
|
322
|
-
}
|
|
323
|
-
|
|
324
|
-
/** Every (state, signal) pair the machine can act on. Used to fuzz the table. */
|
|
325
|
-
function transitions() {
|
|
326
|
-
const out = [];
|
|
327
|
-
for (const state of STATES) {
|
|
328
|
-
for (const signal of Object.keys(TABLE[state] || {})) {
|
|
329
|
-
out.push({ state, signal });
|
|
330
|
-
}
|
|
331
|
-
}
|
|
332
|
-
return out;
|
|
333
|
-
}
|
|
334
|
-
|
|
335
|
-
module.exports = {
|
|
336
|
-
STATES,
|
|
337
|
-
SIGNALS,
|
|
338
|
-
ACTIVITY_SIGNALS,
|
|
339
|
-
EXPRESSIONS,
|
|
340
|
-
TABLE,
|
|
341
|
-
DEFAULT_IDLE_TIMEOUT_MS,
|
|
342
|
-
isState,
|
|
343
|
-
expressionFor,
|
|
344
|
-
createMachine,
|
|
345
|
-
transitions,
|
|
346
|
-
};
|
|
3
|
+
"use strict";const STATES=Object.freeze(["idle","listening","thinking","reading","writing","running","awaiting-you","celebrating","dozing","error"]),SIGNALS=Object.freeze(["turn.start","turn.stream","turn.end","tool.read","tool.write","tool.run","approval.card","approval.decided","queue.progress","sync.error","sync.ok","celebrate.done","poke","reset","idle.timeout"]),ACTIVITY_SIGNALS=Object.freeze(["turn.start","turn.stream","tool.read","tool.write","tool.run","approval.card","approval.decided","queue.progress","poke"]),DEFAULT_IDLE_TIMEOUT_MS=3e5;function afterTurnEnd(e){return e&&e.green?"celebrating":"idle"}const TABLE=Object.freeze({idle:{"turn.start":"listening","approval.card":"awaiting-you","queue.progress":"running","idle.timeout":"dozing","sync.error":"error",reset:"idle"},listening:{"turn.start":"listening","turn.stream":"thinking","tool.read":"reading","tool.write":"writing","tool.run":"running","approval.card":"awaiting-you","turn.end":afterTurnEnd,"sync.error":"error",reset:"idle"},thinking:{"turn.stream":"thinking","tool.read":"reading","tool.write":"writing","tool.run":"running","approval.card":"awaiting-you","turn.end":afterTurnEnd,"sync.error":"error",reset:"idle"},reading:{"tool.read":"reading","tool.write":"writing","tool.run":"running","turn.stream":"thinking","approval.card":"awaiting-you","turn.end":afterTurnEnd,"sync.error":"error",reset:"idle"},writing:{"tool.write":"writing","tool.read":"reading","tool.run":"running","turn.stream":"thinking","approval.card":"awaiting-you","turn.end":afterTurnEnd,"sync.error":"error",reset:"idle"},running:{"tool.run":"running","tool.read":"reading","tool.write":"writing","queue.progress":"running","turn.stream":"thinking","approval.card":"awaiting-you","turn.end":afterTurnEnd,"sync.error":"error",reset:"idle"},"awaiting-you":{"approval.card":"awaiting-you","approval.decided":"thinking","turn.end":afterTurnEnd,"sync.error":"error",reset:"idle"},celebrating:{"celebrate.done":"idle","turn.start":"listening","sync.error":"error",reset:"idle"},dozing:{poke:"idle","turn.start":"listening","approval.card":"awaiting-you","sync.error":"error",reset:"idle"},error:{reset:"idle","sync.ok":"idle","turn.start":"listening","sync.error":"error"}}),EXPRESSIONS=Object.freeze({idle:"neutral",listening:"attentive",thinking:"focused",reading:"focused",writing:"focused",running:"focused","awaiting-you":"waiting",celebrating:"happy",dozing:"sleepy",error:"concerned"});function isState(e){return STATES.includes(e)}function expressionFor(e){return EXPRESSIONS[e]||"neutral"}function createMachine(e){const r=e&&"object"==typeof e?e:{},t=Number.isFinite(r.idleTimeoutMs)&&r.idleTimeoutMs>0?r.idleTimeoutMs:3e5;let n=isState(r.state)?r.state:"idle",i=Number.isFinite(r.now)?r.now:0,o=i,a=0;const s=Number.isFinite(r.historyLimit)&&r.historyLimit>0?r.historyLimit:64,u=[],l=new Set;function d(e,r){const t=n,a=(TABLE[n]||{})[String(e)];let d=n;"function"==typeof a?d=a(r||{}):"string"==typeof a&&(d=a),isState(d)||(d=n);const g=d!==t;return g&&(n=d),ACTIVITY_SIGNALS.includes(String(e))&&(o=i),function(e){u.push(e),u.length>s&&u.shift();for(const r of l)try{r(e)}catch{}}({signal:String(e),prev:t,state:n,changed:g,meta:r||null,t:i}),{changed:g,state:n,prev:t,signal:String(e)}}return{get state(){return n},get expression(){return expressionFor(n)},get lastActivity(){return o},get ticks(){return a},history:u,send:d,tick:function(e){return Number.isFinite(e)&&(i=Math.max(i,e)),a+=1,"idle"!==n||i-o<t?{changed:!1,state:n,prev:n,signal:null}:d("idle.timeout")},setNow:e=>(Number.isFinite(e)&&(i=Math.max(i,e)),i),on(e){if("function"!=typeof e)throw new TypeError("listener must be a function");return l.add(e),()=>l.delete(e)},reset:e=>(l.clear(),u.length=0,n="idle",o=Number.isFinite(e)?e:i,n)}}function transitions(){const e=[];for(const r of STATES)for(const t of Object.keys(TABLE[r]||{}))e.push({state:r,signal:t});return e}module.exports={STATES:STATES,SIGNALS:SIGNALS,ACTIVITY_SIGNALS:ACTIVITY_SIGNALS,EXPRESSIONS:EXPRESSIONS,TABLE:TABLE,DEFAULT_IDLE_TIMEOUT_MS:3e5,isState:isState,expressionFor:expressionFor,createMachine:createMachine,transitions:transitions};
|