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.
Files changed (96) hide show
  1. package/SOURCE-OFFER.txt +9 -0
  2. package/bin/aegis.js +1 -32
  3. package/lib/account-key.js +1 -56
  4. package/lib/auto.js +1 -430
  5. package/lib/avatar/companion.js +1 -698
  6. package/lib/avatar/cosmetics.js +1 -532
  7. package/lib/avatar/events.js +1 -344
  8. package/lib/avatar/holder-turn.js +1 -350
  9. package/lib/avatar/holders.js +1 -612
  10. package/lib/avatar/identity.js +1 -641
  11. package/lib/avatar/level.js +1 -368
  12. package/lib/avatar/mirror.js +1 -524
  13. package/lib/avatar/persona.js +1 -388
  14. package/lib/avatar/profile.js +1 -977
  15. package/lib/avatar/register.js +1 -307
  16. package/lib/avatar/store.js +1 -472
  17. package/lib/avatar/tiers.js +1 -38
  18. package/lib/avatar/transfer.js +1 -415
  19. package/lib/avatar/turn.js +1 -216
  20. package/lib/avatar/voice.js +1 -328
  21. package/lib/avatar/xp.js +1 -404
  22. package/lib/deep-link.js +1 -120
  23. package/lib/fingerprint.js +2 -347
  24. package/lib/google-connect.js +1 -169
  25. package/lib/local/agents.js +1 -105
  26. package/lib/local/autonomous.js +1 -1045
  27. package/lib/local/claude-pro.js +1 -993
  28. package/lib/local/context.js +1 -95
  29. package/lib/local/engine.js +1 -3228
  30. package/lib/local/entitlement.js +1 -654
  31. package/lib/local/git-scope.js +1 -228
  32. package/lib/local/local.js +1 -2135
  33. package/lib/local/prompt.js +1 -275
  34. package/lib/local/queue.js +1 -560
  35. package/lib/local/session-rounds.js +1 -208
  36. package/lib/local/shell.js +1 -680
  37. package/lib/local/telemetry.js +1 -470
  38. package/lib/local/tools.js +1 -1238
  39. package/lib/local/turn-guard.js +1 -300
  40. package/lib/local/worktree-lock.js +1 -235
  41. package/lib/provider-key-mirror.js +1 -252
  42. package/lib/quick-launcher.js +1 -84
  43. package/lib/settings.js +1 -890
  44. package/lib/sync/memory-queue.js +1 -60
  45. package/lib/sync/persist-gate.js +1 -148
  46. package/lib/sync/sessions.js +1 -106
  47. package/lib/window-state.js +1 -66
  48. package/main.js +3 -4390
  49. package/package.json +3 -5
  50. package/preload.js +3 -628
  51. package/renderer/app.js +1 -6560
  52. package/renderer/avatar/assemble.js +1 -335
  53. package/renderer/avatar/avatar.js +1 -380
  54. package/renderer/avatar/cards.js +1 -141
  55. package/renderer/avatar/hud.js +1 -206
  56. package/renderer/avatar/machine.js +1 -213
  57. package/renderer/avatar/pane.js +1 -439
  58. package/renderer/avatar/parts.js +1 -437
  59. package/renderer/budget.js +1 -168
  60. package/renderer/companion.js +1 -223
  61. package/renderer/diff.js +1 -359
  62. package/renderer/diffview.js +1 -336
  63. package/renderer/fingerprint.js +3 -720
  64. package/renderer/markdown.js +1 -168
  65. package/renderer/provider-toolbar.js +1 -134
  66. package/renderer/providers.js +1 -259
  67. package/renderer/quick.js +1 -191
  68. package/renderer/stream-policy.js +1 -306
  69. package/renderer/transcript-view.js +1 -323
  70. package/renderer/usage.js +1 -1239
  71. package/vendor/aegis.js +1 -2054
  72. package/vendor/attest.js +1 -442
  73. package/vendor/authorship-key.js +1 -410
  74. package/vendor/auto-provision.js +1 -134
  75. package/vendor/brain-catalog.js +1 -268
  76. package/vendor/byok-catalog.js +1 -230
  77. package/vendor/credentials.js +1 -494
  78. package/vendor/device-fingerprint.js +1 -141
  79. package/vendor/drive-connect.js +1 -85
  80. package/vendor/drive-upload.js +1 -174
  81. package/vendor/env-file.js +1 -1273
  82. package/vendor/fingerprint.js +1 -1064
  83. package/vendor/foreign-memory.js +1 -669
  84. package/vendor/google-signin.js +1 -125
  85. package/vendor/hooks.js +1 -430
  86. package/vendor/model-discovery.js +1 -330
  87. package/vendor/platform.js +1 -937
  88. package/vendor/prefs.js +1 -351
  89. package/vendor/presence-constants.js +1 -107
  90. package/vendor/presence.js +1 -438
  91. package/vendor/record-vault.js +3 -1055
  92. package/vendor/session-store.js +1 -548
  93. package/vendor/topup.js +1 -297
  94. package/vendor/touch.js +1 -458
  95. package/vendor/update.js +1 -704
  96. package/vendor/user-models.js +1 -381
@@ -1,370 +1,3 @@
1
1
  // SPDX-License-Identifier: AGPL-3.0-or-later
2
2
  // Copyright (C) 2026 Niklas Borneklint — AEGIS Code (aegiscode) / Aegiscloud.
3
- // AEGIS Code (aegiscode) — source: https://github.com/aegisinfo/aegiscode-plugin
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
- * level.js — what a level actually buys.
10
- *
11
- * RULE ZERO, and the reason this module is a pure function instead of a feature
12
- * flag soup: **a bonus may only be a knob the engine already takes.** Levelling
13
- * never invents a code path; it widens the values of parameters turn assembly,
14
- * the tool host and the companion behaviours already read. Recall breadth is
15
- * `memorySearch`'s `limit`. The model floor is the model picker's minimum. The
16
- * queue drain is a tool grant the host already understands. If a proposed bonus
17
- * cannot be expressed as one of those, it is not a bonus — it is a new feature
18
- * wearing a level as a disguise.
19
- *
20
- * `capabilities(level)` is therefore the ONE object the rest of the app reads,
21
- * and it is deliberately written so the safety-relevant entries are *not* a
22
- * function of level at all: `approvals.write`, `approvals.shell`,
23
- * `approvals.network` and `approvals.offDevice` are literally the same frozen
24
- * values at every level. That is what makes the carve-outs below assertable
25
- * rather than merely promised — there is no branch to get wrong.
26
- *
27
- * The carve-outs (asserted in `test/avatar-level.test.mjs` AND enforced at
28
- * runtime by `verifyInvariants`, which main calls at boot):
29
- *
30
- * - Approval gates for writes, shell and network are level-independent.
31
- * Levelling must never widen blast radius.
32
- * - `memoryWriteback` is always true and never gated on level: memory is the
33
- * source of XP, so gating it would be a feedback deadlock.
34
- * - Nothing level-gated may send data off-device (publishing, posting, email).
35
- * - `AEGIS_*` env overrides win over capabilities for automation and CI — and
36
- * they too cannot widen write/shell/network approval.
37
- *
38
- * Pure: no fs, no Electron, no process access except the `env` the caller
39
- * passes.
40
- */
41
-
42
- const { TIERS, tierFor, tierIndex } = require('./tiers.js');
43
-
44
- /**
45
- * The four approval classes that are NOT a function of level. Frozen once and
46
- * shared by every capabilities object, so `caps.approvals.write` is the same
47
- * string for a brand-new install and a level-40 archivist.
48
- */
49
- const LEVEL_INDEPENDENT_APPROVALS = Object.freeze({
50
- write: 'ask',
51
- shell: 'ask',
52
- network: 'ask',
53
- offDevice: 'ask',
54
- });
55
-
56
- /** Classes that may ever be auto-approved. Writes/shell/network never appear. */
57
- const AUTO_APPROVABLE_CLASSES = Object.freeze(['read', 'diff']);
58
-
59
- /** Levels at which each tier's headline bonus lands. Kept next to the tier table. */
60
- const GATES = Object.freeze({
61
- hints: 2,
62
- brief: 5,
63
- ideaLog: 5,
64
- autoApproveDiff: 10,
65
- sessionReflection: 10,
66
- voice: 10,
67
- reasoningFloor: 20,
68
- queueDrain: 20,
69
- importAssistant: 35,
70
- });
71
-
72
- /**
73
- * Cosmetic unlocks. Cosmetics are the ONLY thing this product may ever sell, so
74
- * the free/paid line is expressed here as data: an id with a `paid` flag and
75
- * nothing else. No unlock in this table is allowed to gate XP, a level, recall
76
- * breadth, or approval friction — `verifyInvariants` checks the *capability*
77
- * side of that; the table itself only ever lists presentation ids.
78
- */
79
- const UNLOCKS = Object.freeze([
80
- Object.freeze({ id: 'expression.core', level: 1, kind: 'expressions', paid: false, label: 'Core expressions' }),
81
- Object.freeze({ id: 'outfit.hoodie', level: 1, kind: 'outfit', paid: false, label: 'Hoodie' }),
82
- Object.freeze({ id: 'outfit.tshirt', level: 1, kind: 'outfit', paid: false, label: 'T-shirt' }),
83
- Object.freeze({ id: 'palette.amber', level: 1, kind: 'palette', paid: false, label: 'Amber' }),
84
- Object.freeze({ id: 'outfit.headphones', level: 5, kind: 'outfit', paid: false, label: 'Headphones' }),
85
- Object.freeze({ id: 'expression.trusted', level: 5, kind: 'expressions', paid: false, label: 'Trusted expressions' }),
86
- Object.freeze({ id: 'palette.slate', level: 5, kind: 'palette', paid: false, label: 'Slate' }),
87
- Object.freeze({ id: 'outfit.layers', level: 5, kind: 'outfit', paid: false, label: 'Outfit layers' }),
88
- Object.freeze({ id: 'expression.companion', level: 10, kind: 'expressions', paid: false, label: 'Companion expressions' }),
89
- Object.freeze({ id: 'voice.core', level: 10, kind: 'voice', paid: false, label: 'Voice (local)' }),
90
- Object.freeze({ id: 'expression.confidant', level: 20, kind: 'expressions', paid: false, label: 'Confidant expressions' }),
91
- Object.freeze({ id: 'outfit.seasonal', level: 20, kind: 'outfit', paid: true, label: 'Seasonal set' }),
92
- Object.freeze({ id: 'expression.archivist', level: 35, kind: 'expressions', paid: false, label: 'Archivist expressions' }),
93
- Object.freeze({ id: 'outfit.archive', level: 35, kind: 'outfit', paid: true, label: 'Archive set' }),
94
- ]);
95
-
96
- const PROACTIVITY_ORDER = Object.freeze(['off', 'hints', 'brief']);
97
-
98
- function clampLevel(level) {
99
- const n = Math.floor(typeof level === 'number' && Number.isFinite(level) ? level : 1);
100
- return Math.max(1, n);
101
- }
102
-
103
- function atLeast(level, gate) {
104
- return clampLevel(level) >= gate;
105
- }
106
-
107
- /** Ids unlocked at or below `level`. */
108
- function unlockedBy(level) {
109
- const n = clampLevel(level);
110
- return UNLOCKS.filter((u) => u.level <= n).map((u) => u.id);
111
- }
112
-
113
- /** The next unlock(s) to dangle in the HUD, or null at the top of the table. */
114
- function nextUnlock(level) {
115
- const n = clampLevel(level);
116
- const ahead = UNLOCKS.filter((u) => u.level > n);
117
- if (!ahead.length) return null;
118
- const level_ = Math.min(...ahead.map((u) => u.level));
119
- return {
120
- level: level_,
121
- unlocks: ahead.filter((u) => u.level === level_).map((u) => ({ id: u.id, label: u.label, kind: u.kind, paid: u.paid })),
122
- };
123
- }
124
-
125
- function lowerProactivity(a, b) {
126
- const ia = PROACTIVITY_ORDER.indexOf(a);
127
- const ib = PROACTIVITY_ORDER.indexOf(b);
128
- if (ia < 0) return b;
129
- if (ib < 0) return a;
130
- return PROACTIVITY_ORDER[Math.min(ia, ib)];
131
- }
132
-
133
- /** The proactivity ceiling a level earns, before the user's preference lowers it. */
134
- function proactivityCeiling(level) {
135
- if (atLeast(level, GATES.brief)) return 'brief';
136
- if (atLeast(level, GATES.hints)) return 'hints';
137
- return 'off';
138
- }
139
-
140
- /**
141
- * The one object the rest of the app reads.
142
- *
143
- * @param {number} level
144
- * @param {object} [opts]
145
- * @param {'off'|'hints'|'brief'} [opts.userProactivity] the user's own setting.
146
- * A preference can only ever LOWER proactivity — a level is not something the
147
- * user can talk themselves out of, but a chatty companion is.
148
- */
149
- function capabilities(level, opts = {}) {
150
- const n = clampLevel(level);
151
- const tier = tierFor(n);
152
- const autoApproveClasses = AUTO_APPROVABLE_CLASSES.filter(
153
- (cls) => cls === 'read' || atLeast(n, GATES.autoApproveDiff),
154
- );
155
-
156
- const approvals = Object.freeze({
157
- read: 'auto',
158
- diff: autoApproveClasses.includes('diff') ? 'auto' : 'ask',
159
- ...LEVEL_INDEPENDENT_APPROVALS,
160
- });
161
-
162
- const proactivity = opts.userProactivity === undefined
163
- ? proactivityCeiling(n)
164
- : lowerProactivity(proactivityCeiling(n), opts.userProactivity);
165
-
166
- return Object.freeze({
167
- level: n,
168
- tier: tier.name,
169
- tierIndex: tierIndex(n),
170
-
171
- // recall breadth — literally memorySearch's limit/token budget
172
- recallEntries: 4 + Math.floor(n / 2),
173
- recallTokens: 1500 + 250 * n,
174
-
175
- // never gated (feedback deadlock) — see header
176
- memoryWriteback: true,
177
-
178
- autoApproveClasses: Object.freeze(autoApproveClasses.slice()),
179
- approvals,
180
-
181
- toolGrants: Object.freeze(atLeast(n, GATES.queueDrain) ? ['queue.drain'] : []),
182
- modelFloor: atLeast(n, GATES.reasoningFloor) ? 'reasoning' : null,
183
-
184
- proactivity,
185
- proactivityCeiling: proactivityCeiling(n),
186
-
187
- companion: Object.freeze({
188
- morningBrief: atLeast(n, GATES.brief),
189
- ideaLog: atLeast(n, GATES.ideaLog),
190
- sessionReflection: atLeast(n, GATES.sessionReflection),
191
- voice: atLeast(n, GATES.voice),
192
- queueDrain: atLeast(n, GATES.queueDrain),
193
- importAssistant: atLeast(n, GATES.importAssistant),
194
- }),
195
-
196
- cosmeticsUnlocked: Object.freeze(unlockedBy(n)),
197
- offDeviceGrants: Object.freeze([]),
198
- });
199
- }
200
-
201
- // ---------------------------------------------------------------------------
202
- // carve-outs
203
- // ---------------------------------------------------------------------------
204
-
205
- /**
206
- * Which invariants this capabilities object violates. Empty array = safe.
207
- * `verifyInvariants` runs this over the whole level range; main runs it at boot
208
- * so a future edit that gates a write approval on level fails loudly at launch
209
- * instead of silently weakening the gate.
210
- */
211
- function carveOuts(caps) {
212
- const violations = [];
213
- if (!caps || typeof caps !== 'object') return ['capabilities missing'];
214
-
215
- for (const cls of ['write', 'shell', 'network', 'offDevice']) {
216
- const got = caps.approvals && caps.approvals[cls];
217
- if (got !== 'ask') violations.push(`approval for ${cls} must stay "ask", got ${JSON.stringify(got)}`);
218
- }
219
- for (const cls of caps.autoApproveClasses || []) {
220
- if (!AUTO_APPROVABLE_CLASSES.includes(cls)) {
221
- violations.push(`class ${cls} is not auto-approvable at any level`);
222
- }
223
- }
224
- for (const cls of ['write', 'shell', 'network', 'offDevice']) {
225
- if ((caps.autoApproveClasses || []).includes(cls)) {
226
- violations.push(`class ${cls} must never be auto-approved`);
227
- }
228
- }
229
- if (caps.memoryWriteback !== true) {
230
- violations.push('memoryWriteback must stay true at every level (XP would deadlock)');
231
- }
232
- if (!Array.isArray(caps.offDeviceGrants) || caps.offDeviceGrants.length) {
233
- violations.push('nothing level-gated may send data off-device');
234
- }
235
- for (const grant of caps.toolGrants || []) {
236
- if (/publish|post|email|upload|deploy|broadcast/i.test(String(grant))) {
237
- violations.push(`tool grant ${grant} reaches off-device`);
238
- }
239
- }
240
- return violations;
241
- }
242
-
243
- /** Throws on the first violation. Used by tests and by main at boot. */
244
- function assertCarveOuts(caps) {
245
- const bad = carveOuts(caps);
246
- if (bad.length) throw new Error(`avatar capabilities violate a carve-out: ${bad.join('; ')}`);
247
- return caps;
248
- }
249
-
250
- /**
251
- * Sweep a level range and assert every invariant, plus monotonicity: a higher
252
- * level may never grant LESS than a lower one (a bonus that disappears as you
253
- * level is a bug that reads as a punishment).
254
- */
255
- function verifyInvariants(levels) {
256
- const list = levels || Array.from({ length: 60 }, (_, i) => i + 1);
257
- const violations = [];
258
- let prev = null;
259
- for (const level of list) {
260
- const caps = capabilities(level);
261
- for (const v of carveOuts(caps)) violations.push(`L${level}: ${v}`);
262
- if (prev) {
263
- const checks = [
264
- ['recallEntries', (c) => c.recallEntries],
265
- ['recallTokens', (c) => c.recallTokens],
266
- ];
267
- for (const [name, get] of checks) {
268
- if (get(caps) < get(prev)) violations.push(`L${level}: ${name} shrank from ${get(prev)} to ${get(caps)}`);
269
- }
270
- for (const cls of AUTO_APPROVABLE_CLASSES) {
271
- const was = prev.autoApproveClasses.includes(cls);
272
- const now = caps.autoApproveClasses.includes(cls);
273
- if (was && !now) violations.push(`L${level}: auto-approve for ${cls} was taken away`);
274
- }
275
- if (prev.modelFloor === 'reasoning' && caps.modelFloor !== 'reasoning') {
276
- violations.push(`L${level}: reasoning model floor was taken away`);
277
- }
278
- if (PROACTIVITY_ORDER.indexOf(caps.proactivity) < PROACTIVITY_ORDER.indexOf(prev.proactivity)) {
279
- violations.push(`L${level}: proactivity dropped to ${caps.proactivity}`);
280
- }
281
- }
282
- prev = caps;
283
- }
284
- return violations;
285
- }
286
-
287
- // ---------------------------------------------------------------------------
288
- // env overrides (automation / CI)
289
- // ---------------------------------------------------------------------------
290
-
291
- const TRUTHY = new Set(['1', 'true', 'yes', 'on']);
292
-
293
- /**
294
- * `AEGIS_*` wins over capabilities, because a headless CI run must not have its
295
- * recall breadth decided by someone's XP. Overrides may widen recall and lower
296
- * proactivity freely; they may NOT touch the level-independent approvals, and an
297
- * attempt to auto-approve writes/shell/network is ignored (and reported in
298
- * `ignored`) rather than honoured.
299
- */
300
- function applyEnvOverrides(caps, env) {
301
- const e = env || {};
302
- const ignored = [];
303
- const out = Object.assign({}, caps);
304
- const int = (v) => {
305
- const n = Number.parseInt(String(v), 10);
306
- return Number.isFinite(n) && n > 0 ? n : null;
307
- };
308
-
309
- if (e.AEGIS_AVATAR_DISABLE !== undefined && TRUTHY.has(String(e.AEGIS_AVATAR_DISABLE).toLowerCase())) {
310
- out.proactivity = 'off';
311
- out.modelFloor = null;
312
- out.toolGrants = Object.freeze([]);
313
- }
314
-
315
- const entries = int(e.AEGIS_RECALL_ENTRIES);
316
- if (entries !== null) out.recallEntries = entries;
317
- const tokens = int(e.AEGIS_RECALL_TOKENS);
318
- if (tokens !== null) out.recallTokens = tokens;
319
-
320
- if (e.AEGIS_PROACTIVITY !== undefined) {
321
- const want = String(e.AEGIS_PROACTIVITY).toLowerCase();
322
- if (PROACTIVITY_ORDER.includes(want)) {
323
- out.proactivity = lowerProactivity(out.proactivity, want);
324
- } else {
325
- ignored.push(`AEGIS_PROACTIVITY=${e.AEGIS_PROACTIVITY}`);
326
- }
327
- }
328
-
329
- if (e.AEGIS_MODEL_FLOOR !== undefined) {
330
- const want = String(e.AEGIS_MODEL_FLOOR).toLowerCase();
331
- if (want === 'reasoning') out.modelFloor = 'reasoning';
332
- else if (want === 'off' || want === 'none') out.modelFloor = null;
333
- else ignored.push(`AEGIS_MODEL_FLOOR=${e.AEGIS_MODEL_FLOOR}`);
334
- }
335
-
336
- if (e.AEGIS_AUTO_APPROVE !== undefined) {
337
- const want = String(e.AEGIS_AUTO_APPROVE)
338
- .split(',')
339
- .map((s) => s.trim().toLowerCase())
340
- .filter(Boolean);
341
- const dangerous = want.filter((c) => !AUTO_APPROVABLE_CLASSES.includes(c));
342
- if (dangerous.length) ignored.push(`AEGIS_AUTO_APPROVE=${dangerous.join(',')}`);
343
- const safe = want.filter((c) => AUTO_APPROVABLE_CLASSES.includes(c));
344
- if (safe.length) {
345
- out.autoApproveClasses = Object.freeze(Array.from(new Set([...(out.autoApproveClasses || []), ...safe])));
346
- out.approvals = Object.freeze({ ...out.approvals, ...Object.fromEntries(safe.map((c) => [c, 'auto'])) });
347
- }
348
- }
349
-
350
- return { caps: out, ignored };
351
- }
352
-
353
- module.exports = {
354
- LEVEL_INDEPENDENT_APPROVALS,
355
- AUTO_APPROVABLE_CLASSES,
356
- GATES,
357
- UNLOCKS,
358
- PROACTIVITY_ORDER,
359
- TIERS,
360
- tierFor,
361
- tierIndex,
362
- capabilities,
363
- proactivityCeiling,
364
- unlockedBy,
365
- nextUnlock,
366
- carveOuts,
367
- assertCarveOuts,
368
- verifyInvariants,
369
- applyEnvOverrides,
370
- };
3
+ "use strict";const{TIERS:TIERS,tierFor:tierFor,tierIndex:tierIndex}=require("./tiers.js"),LEVEL_INDEPENDENT_APPROVALS=Object.freeze({write:"ask",shell:"ask",network:"ask",offDevice:"ask"}),AUTO_APPROVABLE_CLASSES=Object.freeze(["read","diff"]),GATES=Object.freeze({hints:2,brief:5,ideaLog:5,autoApproveDiff:10,sessionReflection:10,voice:10,reasoningFloor:20,queueDrain:20,importAssistant:35}),UNLOCKS=Object.freeze([Object.freeze({id:"expression.core",level:1,kind:"expressions",paid:!1,label:"Core expressions"}),Object.freeze({id:"outfit.hoodie",level:1,kind:"outfit",paid:!1,label:"Hoodie"}),Object.freeze({id:"outfit.tshirt",level:1,kind:"outfit",paid:!1,label:"T-shirt"}),Object.freeze({id:"palette.amber",level:1,kind:"palette",paid:!1,label:"Amber"}),Object.freeze({id:"outfit.headphones",level:5,kind:"outfit",paid:!1,label:"Headphones"}),Object.freeze({id:"expression.trusted",level:5,kind:"expressions",paid:!1,label:"Trusted expressions"}),Object.freeze({id:"palette.slate",level:5,kind:"palette",paid:!1,label:"Slate"}),Object.freeze({id:"outfit.layers",level:5,kind:"outfit",paid:!1,label:"Outfit layers"}),Object.freeze({id:"expression.companion",level:10,kind:"expressions",paid:!1,label:"Companion expressions"}),Object.freeze({id:"voice.core",level:10,kind:"voice",paid:!1,label:"Voice (local)"}),Object.freeze({id:"expression.confidant",level:20,kind:"expressions",paid:!1,label:"Confidant expressions"}),Object.freeze({id:"outfit.seasonal",level:20,kind:"outfit",paid:!0,label:"Seasonal set"}),Object.freeze({id:"expression.archivist",level:35,kind:"expressions",paid:!1,label:"Archivist expressions"}),Object.freeze({id:"outfit.archive",level:35,kind:"outfit",paid:!0,label:"Archive set"})]),PROACTIVITY_ORDER=Object.freeze(["off","hints","brief"]);function clampLevel(e){const t=Math.floor("number"==typeof e&&Number.isFinite(e)?e:1);return Math.max(1,t)}function atLeast(e,t){return clampLevel(e)>=t}function unlockedBy(e){const t=clampLevel(e);return UNLOCKS.filter((e=>e.level<=t)).map((e=>e.id))}function nextUnlock(e){const t=clampLevel(e),i=UNLOCKS.filter((e=>e.level>t));if(!i.length)return null;const o=Math.min(...i.map((e=>e.level)));return{level:o,unlocks:i.filter((e=>e.level===o)).map((e=>({id:e.id,label:e.label,kind:e.kind,paid:e.paid})))}}function lowerProactivity(e,t){const i=PROACTIVITY_ORDER.indexOf(e),o=PROACTIVITY_ORDER.indexOf(t);return i<0?t:o<0?e:PROACTIVITY_ORDER[Math.min(i,o)]}function proactivityCeiling(e){return atLeast(e,GATES.brief)?"brief":atLeast(e,GATES.hints)?"hints":"off"}function capabilities(e,t={}){const i=clampLevel(e),o=tierFor(i),r=AUTO_APPROVABLE_CLASSES.filter((e=>"read"===e||atLeast(i,GATES.autoApproveDiff))),a=Object.freeze({read:"auto",diff:r.includes("diff")?"auto":"ask",...LEVEL_INDEPENDENT_APPROVALS}),s=void 0===t.userProactivity?proactivityCeiling(i):lowerProactivity(proactivityCeiling(i),t.userProactivity);return Object.freeze({level:i,tier:o.name,tierIndex:tierIndex(i),recallEntries:4+Math.floor(i/2),recallTokens:1500+250*i,memoryWriteback:!0,autoApproveClasses:Object.freeze(r.slice()),approvals:a,toolGrants:Object.freeze(atLeast(i,GATES.queueDrain)?["queue.drain"]:[]),modelFloor:atLeast(i,GATES.reasoningFloor)?"reasoning":null,proactivity:s,proactivityCeiling:proactivityCeiling(i),companion:Object.freeze({morningBrief:atLeast(i,GATES.brief),ideaLog:atLeast(i,GATES.ideaLog),sessionReflection:atLeast(i,GATES.sessionReflection),voice:atLeast(i,GATES.voice),queueDrain:atLeast(i,GATES.queueDrain),importAssistant:atLeast(i,GATES.importAssistant)}),cosmeticsUnlocked:Object.freeze(unlockedBy(i)),offDeviceGrants:Object.freeze([])})}function carveOuts(e){const t=[];if(!e||"object"!=typeof e)return["capabilities missing"];for(const i of["write","shell","network","offDevice"]){const o=e.approvals&&e.approvals[i];"ask"!==o&&t.push(`approval for ${i} must stay "ask", got ${JSON.stringify(o)}`)}for(const i of e.autoApproveClasses||[])AUTO_APPROVABLE_CLASSES.includes(i)||t.push(`class ${i} is not auto-approvable at any level`);for(const i of["write","shell","network","offDevice"])(e.autoApproveClasses||[]).includes(i)&&t.push(`class ${i} must never be auto-approved`);!0!==e.memoryWriteback&&t.push("memoryWriteback must stay true at every level (XP would deadlock)"),Array.isArray(e.offDeviceGrants)&&!e.offDeviceGrants.length||t.push("nothing level-gated may send data off-device");for(const i of e.toolGrants||[])/publish|post|email|upload|deploy|broadcast/i.test(String(i))&&t.push(`tool grant ${i} reaches off-device`);return t}function assertCarveOuts(e){const t=carveOuts(e);if(t.length)throw new Error(`avatar capabilities violate a carve-out: ${t.join("; ")}`);return e}function verifyInvariants(e){const t=e||Array.from({length:60},((e,t)=>t+1)),i=[];let o=null;for(const e of t){const t=capabilities(e);for(const o of carveOuts(t))i.push(`L${e}: ${o}`);if(o){const r=[["recallEntries",e=>e.recallEntries],["recallTokens",e=>e.recallTokens]];for(const[a,s]of r)s(t)<s(o)&&i.push(`L${e}: ${a} shrank from ${s(o)} to ${s(t)}`);for(const r of AUTO_APPROVABLE_CLASSES){const a=o.autoApproveClasses.includes(r),s=t.autoApproveClasses.includes(r);a&&!s&&i.push(`L${e}: auto-approve for ${r} was taken away`)}"reasoning"===o.modelFloor&&"reasoning"!==t.modelFloor&&i.push(`L${e}: reasoning model floor was taken away`),PROACTIVITY_ORDER.indexOf(t.proactivity)<PROACTIVITY_ORDER.indexOf(o.proactivity)&&i.push(`L${e}: proactivity dropped to ${t.proactivity}`)}o=t}return i}const TRUTHY=new Set(["1","true","yes","on"]);function applyEnvOverrides(e,t){const i=t||{},o=[],r=Object.assign({},e),a=e=>{const t=Number.parseInt(String(e),10);return Number.isFinite(t)&&t>0?t:null};void 0!==i.AEGIS_AVATAR_DISABLE&&TRUTHY.has(String(i.AEGIS_AVATAR_DISABLE).toLowerCase())&&(r.proactivity="off",r.modelFloor=null,r.toolGrants=Object.freeze([]));const s=a(i.AEGIS_RECALL_ENTRIES);null!==s&&(r.recallEntries=s);const n=a(i.AEGIS_RECALL_TOKENS);if(null!==n&&(r.recallTokens=n),void 0!==i.AEGIS_PROACTIVITY){const e=String(i.AEGIS_PROACTIVITY).toLowerCase();PROACTIVITY_ORDER.includes(e)?r.proactivity=lowerProactivity(r.proactivity,e):o.push(`AEGIS_PROACTIVITY=${i.AEGIS_PROACTIVITY}`)}if(void 0!==i.AEGIS_MODEL_FLOOR){const e=String(i.AEGIS_MODEL_FLOOR).toLowerCase();"reasoning"===e?r.modelFloor="reasoning":"off"===e||"none"===e?r.modelFloor=null:o.push(`AEGIS_MODEL_FLOOR=${i.AEGIS_MODEL_FLOOR}`)}if(void 0!==i.AEGIS_AUTO_APPROVE){const e=String(i.AEGIS_AUTO_APPROVE).split(",").map((e=>e.trim().toLowerCase())).filter(Boolean),t=e.filter((e=>!AUTO_APPROVABLE_CLASSES.includes(e)));t.length&&o.push(`AEGIS_AUTO_APPROVE=${t.join(",")}`);const a=e.filter((e=>AUTO_APPROVABLE_CLASSES.includes(e)));a.length&&(r.autoApproveClasses=Object.freeze(Array.from(new Set([...r.autoApproveClasses||[],...a]))),r.approvals=Object.freeze({...r.approvals,...Object.fromEntries(a.map((e=>[e,"auto"])))}))}return{caps:r,ignored:o}}module.exports={LEVEL_INDEPENDENT_APPROVALS:LEVEL_INDEPENDENT_APPROVALS,AUTO_APPROVABLE_CLASSES:AUTO_APPROVABLE_CLASSES,GATES:GATES,UNLOCKS:UNLOCKS,PROACTIVITY_ORDER:PROACTIVITY_ORDER,TIERS:TIERS,tierFor:tierFor,tierIndex:tierIndex,capabilities:capabilities,proactivityCeiling:proactivityCeiling,unlockedBy:unlockedBy,nextUnlock:nextUnlock,carveOuts:carveOuts,assertCarveOuts:assertCarveOuts,verifyInvariants:verifyInvariants,applyEnvOverrides:applyEnvOverrides};