aegis-desktop 1.0.10 → 1.0.13
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/lib/auto.js +11 -7
- package/lib/fingerprint.js +76 -17
- package/lib/google-connect.js +171 -0
- package/lib/local/claude-pro.js +134 -11
- package/lib/local/engine.js +111 -26
- package/lib/local/prompt.js +67 -37
- package/lib/local/tools.js +49 -13
- package/lib/settings.js +65 -24
- package/main.js +270 -36
- package/package.json +5 -2
- package/preload.js +45 -12
- package/renderer/app.js +691 -22
- package/renderer/budget.js +18 -9
- package/renderer/companion.js +225 -0
- package/renderer/fingerprint.css +16 -0
- package/renderer/fingerprint.js +136 -2
- package/renderer/index.html +302 -121
- package/renderer/provider-toolbar.js +136 -0
- package/renderer/providers.js +261 -0
- package/renderer/style.css +547 -171
- package/vendor/aegis.js +169 -3
- package/vendor/credentials.js +9 -2
- package/vendor/drive-connect.js +87 -0
- package/vendor/drive-upload.js +176 -0
- package/vendor/google-signin.js +127 -0
- package/vendor/record-vault.js +431 -113
- package/vendor/update.js +366 -8
package/renderer/index.html
CHANGED
|
@@ -99,61 +99,100 @@
|
|
|
99
99
|
<p class="hint" id="billing-hint"></p>
|
|
100
100
|
</section>
|
|
101
101
|
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
<label for="model-select">Model</label>
|
|
108
|
-
<select id="model-select"></select>
|
|
109
|
-
<!-- Typed model tag. Hidden unless the local class has no list to pick
|
|
110
|
-
from: a daemon that is not running, or one that does not speak
|
|
111
|
-
Ollama's /api/tags (llama.cpp, LM Studio, vLLM). Those still serve
|
|
112
|
-
the OpenAI-compatible chat route, so a typed tag is a working path,
|
|
113
|
-
not a fallback that cannot send. Was a dead <input> left behind
|
|
114
|
-
when the custom-endpoint class was removed. -->
|
|
115
|
-
<input
|
|
116
|
-
type="text"
|
|
117
|
-
id="model-input"
|
|
118
|
-
placeholder="model tag — e.g. llama3.2:3b"
|
|
119
|
-
autocomplete="off"
|
|
120
|
-
spellcheck="false"
|
|
121
|
-
hidden
|
|
122
|
-
/>
|
|
102
|
+
<!-- TWO cards, and the split is the point: a provider the relay's
|
|
103
|
+
catalog names is configured by a KEY alone, while a custom endpoint
|
|
104
|
+
is configured by an ADDRESS. They cannot share one row shape, which
|
|
105
|
+
is what this markup used to require.
|
|
123
106
|
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
<
|
|
156
|
-
|
|
107
|
+
- #provider-paths-card (catalog): the class/model/effort bar, one
|
|
108
|
+
key row per provider `GET /api/v1/byok/providers` names, the
|
|
109
|
+
user's own model declarations, and the list of every route.
|
|
110
|
+
- #custom-provider-card: a base URL, a key and a model id for the
|
|
111
|
+
`custom` provider — a vendor this catalog does not name, or a
|
|
112
|
+
self-hosted OpenAI-compatible server. aegis1 reads that address
|
|
113
|
+
per request (`_byok_custom_base_url`), and no other provider
|
|
114
|
+
takes one, so its row cannot be a key row with a second field
|
|
115
|
+
bolted on: it is a different decision, and it gets its own card.
|
|
116
|
+
|
|
117
|
+
Before the split, `custom` came back from the catalog as an ordinary
|
|
118
|
+
key row with no base-URL field at all — a route that could be given a
|
|
119
|
+
key and then never dialed, since the server refuses a custom call
|
|
120
|
+
that names no address. Named here rather than in a code comment
|
|
121
|
+
because the arrangement of these two sections IS the fix.
|
|
122
|
+
|
|
123
|
+
Rows in the catalog card are painted by provider-toolbar.js from
|
|
124
|
+
providers.js; this markup is the container only.
|
|
125
|
+
|
|
126
|
+
Deliberately NOT in either list: the φ(α) seal (not a provider — it
|
|
127
|
+
publishes a record of a repo) and the autonomous fan-out (a property
|
|
128
|
+
of a turn on the pooled class, reaching no route the pooled row does
|
|
129
|
+
not already name). Asserted in test/renderer-providers.test.mjs. -->
|
|
130
|
+
<section class="card" id="provider-paths-card">
|
|
131
|
+
<h2>Model & provider</h2>
|
|
132
|
+
|
|
133
|
+
<!-- The bar. Class first, then the model it offers, then the effort
|
|
134
|
+
rung, because that is the order the decision is made in and each
|
|
135
|
+
control is meaningless before the one to its left. -->
|
|
136
|
+
<div class="pt-toolbar" id="pt-toolbar">
|
|
137
|
+
<label class="pt-field" for="class-select">
|
|
138
|
+
<span>provider</span>
|
|
139
|
+
<select id="class-select"></select>
|
|
140
|
+
</label>
|
|
141
|
+
|
|
142
|
+
<label class="pt-field" for="model-select">
|
|
143
|
+
<span>model</span>
|
|
144
|
+
<select id="model-select"></select>
|
|
145
|
+
</label>
|
|
146
|
+
|
|
147
|
+
<!-- Typed model tag. Hidden unless the local class has no list to
|
|
148
|
+
pick from: a daemon that is not running, or one that does not
|
|
149
|
+
speak Ollama's /api/tags (llama.cpp, LM Studio, vLLM). Those
|
|
150
|
+
still serve the OpenAI-compatible chat route, so a typed tag is
|
|
151
|
+
a working path, not a fallback that cannot send. -->
|
|
152
|
+
<input
|
|
153
|
+
type="text"
|
|
154
|
+
id="model-input"
|
|
155
|
+
placeholder="model tag — e.g. llama3.2:3b"
|
|
156
|
+
autocomplete="off"
|
|
157
|
+
spellcheck="false"
|
|
158
|
+
hidden
|
|
159
|
+
/>
|
|
160
|
+
|
|
161
|
+
<!-- The one budget control, for every class and every model.
|
|
162
|
+
|
|
163
|
+
It replaced a "Max tokens" dropdown (1k/4k/16k/64k/300k plus an
|
|
164
|
+
"adaptive" checkbox) that asked the user to state, up front, how
|
|
165
|
+
many tokens an answer would need. That cannot be known from the
|
|
166
|
+
prompt — it is a guess at the length of text nobody has written
|
|
167
|
+
yet — and the guess was wrong in both directions:
|
|
168
|
+
|
|
169
|
+
- the pooled AEGIS Cloud class sizes each call server-side
|
|
170
|
+
from effort (aegis1 services/pool_brain.py pass_budgets:
|
|
171
|
+
low/medium/high -> 16384/32768/65536 tokens total across the
|
|
172
|
+
worker fan-out), and it reads a body max_tokens as a ceiling
|
|
173
|
+
OVER that ladder, so the number on screen was never the
|
|
174
|
+
budget the call ran on;
|
|
175
|
+
- a DeepSeek reasoning model (deepseek-flash, i.e. "Flash 4.1")
|
|
176
|
+
bills hidden chain-of-thought against the same budget as its
|
|
177
|
+
answer, so the dropdown's 4k default was spent before the
|
|
178
|
+
first visible token and the turn came back empty — no error,
|
|
179
|
+
no tool call, just a turn that "completed" with nothing.
|
|
180
|
+
|
|
181
|
+
So the budget is derived from this rung instead. The engine maps
|
|
182
|
+
low/medium/high to 8192/16384/32768 (EFFORT_TOKEN_BUDGET in
|
|
183
|
+
desktop/lib/local/engine.js, mirroring the upstream engine's
|
|
184
|
+
src/backend.js), "auto" leaves the choice to the server, and no
|
|
185
|
+
max_tokens is stated by this app at all. The control is always
|
|
186
|
+
on the bar: with the dropdown gone there is nothing to swap it
|
|
187
|
+
for. -->
|
|
188
|
+
<span class="budget-row" id="effort-row">
|
|
189
|
+
<label for="effort-select" title="Sizes the call's token budget. Output length cannot be predicted from the prompt, so the budget comes from this rung instead of a stated number.">effort</label>
|
|
190
|
+
<select id="effort-select">
|
|
191
|
+
<option value="low">low</option>
|
|
192
|
+
<option value="medium">medium</option>
|
|
193
|
+
<option value="high" selected>high</option>
|
|
194
|
+
</select>
|
|
195
|
+
</span>
|
|
157
196
|
</div>
|
|
158
197
|
|
|
159
198
|
<!-- What the rung above means for the model selected right now, in
|
|
@@ -182,13 +221,113 @@
|
|
|
182
221
|
title="How many reasoning workers this turn fans out to. Every worker is a separate full model call on the same tier, so the turn costs roughly this number plus one times a normal one — higher is not automatically better."
|
|
183
222
|
/>
|
|
184
223
|
</div>
|
|
185
|
-
<p class="hint" id="model-hint"></p>
|
|
186
|
-
</section>
|
|
187
224
|
|
|
188
|
-
|
|
189
|
-
|
|
225
|
+
<!-- The key for whatever the bar is pointed at. These rows are built by
|
|
226
|
+
loadSettings(): one for the local server's base URL (always — it
|
|
227
|
+
needs no key and no account), and one per provider the server's
|
|
228
|
+
catalog names, each saving to `byok:<id>`. The row for the class on
|
|
229
|
+
the bar is the one that matters, and it is here rather than in a
|
|
230
|
+
card of its own so "provider: byok:anthropic" and "paste the
|
|
231
|
+
Anthropic key" are never more than one glance apart.
|
|
232
|
+
|
|
233
|
+
`custom` is built here as a row like any other and then REMOVED by
|
|
234
|
+
loadSettings(), because the card below is where it is configured:
|
|
235
|
+
its address is a field the catalog rows do not have, and a row that
|
|
236
|
+
saved a key with nowhere to send it is the defect this split fixes.
|
|
237
|
+
See #custom-provider-card. -->
|
|
190
238
|
<div id="settings-list"></div>
|
|
191
239
|
<p class="hint" id="settings-hint"></p>
|
|
240
|
+
<p class="hint" id="model-hint"></p>
|
|
241
|
+
|
|
242
|
+
<div class="pt-bar">
|
|
243
|
+
<input
|
|
244
|
+
type="text"
|
|
245
|
+
id="pt-search"
|
|
246
|
+
placeholder="Filter providers, models, URLs…"
|
|
247
|
+
autocomplete="off"
|
|
248
|
+
spellcheck="false"
|
|
249
|
+
/>
|
|
250
|
+
<button type="button" id="pt-refresh" class="ghost-btn">Refresh</button>
|
|
251
|
+
</div>
|
|
252
|
+
<p class="hint" id="pt-count"></p>
|
|
253
|
+
<ul class="pt-list" id="pt-list"></ul>
|
|
254
|
+
<p class="hint" id="pt-hint"></p>
|
|
255
|
+
</section>
|
|
256
|
+
|
|
257
|
+
<!-- A provider the relay's catalog does not name: an OpenAI-compatible
|
|
258
|
+
endpoint you run or subscribe to yourself. Separate from the catalog
|
|
259
|
+
card above because the two ask different things — this one needs an
|
|
260
|
+
ADDRESS as well as a key, and the model id it pins has to be declared
|
|
261
|
+
too (aegis1's `custom` descriptor pins no default_model, so the relay
|
|
262
|
+
catalog offers no custom model row for the picker to show).
|
|
263
|
+
|
|
264
|
+
What saving here writes, and why all three:
|
|
265
|
+
1. `byok:custom` in the settings store — the base URL aegis1 reads
|
|
266
|
+
per call (app.py `_byok_custom_base_url`: https only, no
|
|
267
|
+
loopback/private target) plus the vendor key;
|
|
268
|
+
2. the model id into the user-models file (`client/user-models.js`,
|
|
269
|
+
the same one the CLI's `/model-add` writes), because that file
|
|
270
|
+
is what `listModels('byok')` folds into the picker;
|
|
271
|
+
3. nothing else — no catalog row, no class of its own.
|
|
272
|
+
|
|
273
|
+
The three fields are one decision, so they share one card and one
|
|
274
|
+
Save: a saved key with no address is undialable, and an address with
|
|
275
|
+
no declared model is a route the picker cannot offer. -->
|
|
276
|
+
<section class="card" id="custom-provider-card">
|
|
277
|
+
<h2>Custom provider</h2>
|
|
278
|
+
<p class="hint" id="custom-hint">
|
|
279
|
+
Any OpenAI-compatible endpoint — vLLM, LiteLLM, a gateway, or a vendor
|
|
280
|
+
this build does not know. Saved as <code>byok:custom</code>.
|
|
281
|
+
</p>
|
|
282
|
+
|
|
283
|
+
<label class="pt-field" for="custom-base-url">
|
|
284
|
+
<span>base url (https://…)</span>
|
|
285
|
+
<input
|
|
286
|
+
type="text"
|
|
287
|
+
id="custom-base-url"
|
|
288
|
+
placeholder="https://my-vllm.example/v1"
|
|
289
|
+
autocomplete="off"
|
|
290
|
+
spellcheck="false"
|
|
291
|
+
/>
|
|
292
|
+
</label>
|
|
293
|
+
|
|
294
|
+
<label class="pt-field" for="custom-key">
|
|
295
|
+
<span>api key</span>
|
|
296
|
+
<input
|
|
297
|
+
type="password"
|
|
298
|
+
id="custom-key"
|
|
299
|
+
placeholder="sk-…"
|
|
300
|
+
autocomplete="off"
|
|
301
|
+
/>
|
|
302
|
+
</label>
|
|
303
|
+
|
|
304
|
+
<label class="pt-field" for="custom-model">
|
|
305
|
+
<span>model id</span>
|
|
306
|
+
<input
|
|
307
|
+
type="text"
|
|
308
|
+
id="custom-model"
|
|
309
|
+
placeholder="llama-3-70b"
|
|
310
|
+
autocomplete="off"
|
|
311
|
+
spellcheck="false"
|
|
312
|
+
/>
|
|
313
|
+
</label>
|
|
314
|
+
|
|
315
|
+
<div class="api-key-row">
|
|
316
|
+
<button type="button" id="custom-save" class="ghost-btn">Save provider</button>
|
|
317
|
+
<button type="button" id="custom-remove" class="ghost-btn danger">Remove</button>
|
|
318
|
+
</div>
|
|
319
|
+
<p class="hint" id="custom-status"></p>
|
|
320
|
+
</section>
|
|
321
|
+
|
|
322
|
+
<!-- One line, once a local day, built only from facts this app actually
|
|
323
|
+
holds (companion.js). Dismissing it hides it until tomorrow; the
|
|
324
|
+
button is present in markup so the card never reflows on first paint. -->
|
|
325
|
+
<section class="card" id="companion-card" hidden>
|
|
326
|
+
<h2>Today</h2>
|
|
327
|
+
<p class="companion-line" id="companion-text"></p>
|
|
328
|
+
<div class="companion-row">
|
|
329
|
+
<button type="button" id="companion-dismiss" class="ghost-btn">Got it</button>
|
|
330
|
+
</div>
|
|
192
331
|
</section>
|
|
193
332
|
|
|
194
333
|
<section class="card">
|
|
@@ -245,7 +384,7 @@
|
|
|
245
384
|
type="button"
|
|
246
385
|
class="card-title-btn"
|
|
247
386
|
id="memory-open"
|
|
248
|
-
title="Open the memory inspector — every entry with its source
|
|
387
|
+
title="Open the memory inspector — every entry with its source and tags"
|
|
249
388
|
>
|
|
250
389
|
<span>Memory</span>
|
|
251
390
|
<span class="ctb-expand" aria-hidden="true">⤢</span>
|
|
@@ -273,13 +412,21 @@
|
|
|
273
412
|
<p class="hint" id="memory-hint"></p>
|
|
274
413
|
</section>
|
|
275
414
|
|
|
276
|
-
<section class="card">
|
|
277
|
-
<!--
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
415
|
+
<section class="card" id="queue-card" hidden>
|
|
416
|
+
<!-- HIDDEN ON PURPOSE (user request): the queue is a background worker
|
|
417
|
+
now, not a panel on the desktop. The card stays in the document
|
|
418
|
+
ONLY so the ids app.js and its tests already resolve keep existing —
|
|
419
|
+
`renderQueueState()`, `loadAutoState()` and the queue model pickers
|
|
420
|
+
paint into these nodes, and an absent node turns a paint into a
|
|
421
|
+
throw. Nothing here is visible, focusable or tab-reachable while
|
|
422
|
+
`hidden` is set, and no affordance offers to un-hide it.
|
|
423
|
+
|
|
424
|
+
The worker itself runs from the main process (desktop/lib/auto.js):
|
|
425
|
+
it drains pending tasks on its own cadence and seals a quiet
|
|
426
|
+
session, both switched on by default (settings.js
|
|
427
|
+
`__autoDrain`/`__autoMint`), so queueing a task and walking away is
|
|
428
|
+
the normal path. Turning either off is a settings-store write, not
|
|
429
|
+
a click on this card.
|
|
283
430
|
|
|
284
431
|
The list below is state, not a transcript: the worker's own output
|
|
285
432
|
arrives on queue:progress and is shown as a status line and each
|
|
@@ -361,7 +508,7 @@
|
|
|
361
508
|
<label
|
|
362
509
|
class="flow-toggle"
|
|
363
510
|
for="queue-auto"
|
|
364
|
-
title="Drain pending tasks on a timer, with no click.
|
|
511
|
+
title="Drain pending tasks on a timer, with no click. On by default (this switch only turns it off). Each drain runs a full tool loop on a real model in the task's own checkout — the timer only ever drains tasks already in this queue, and main.js refuses the tick if another host (the CLI, a systemd worker) holds the lock."
|
|
365
512
|
>
|
|
366
513
|
<input type="checkbox" id="queue-auto" />
|
|
367
514
|
<span id="queue-auto-label">drain automatically</span>
|
|
@@ -380,8 +527,17 @@
|
|
|
380
527
|
<div class="queue-upgrade" id="queue-upgrade"></div>
|
|
381
528
|
</section>
|
|
382
529
|
|
|
383
|
-
<section class="card">
|
|
384
|
-
<!--
|
|
530
|
+
<section class="card" id="authorship-card" hidden>
|
|
531
|
+
<!-- HIDDEN ON PURPOSE (user request): sealing is a background worker
|
|
532
|
+
now. The authorship panel is still reachable — ⌘/Ctrl-K style menu
|
|
533
|
+
paths and `window.aegisFingerprint` are unchanged — but this entry
|
|
534
|
+
card no longer sits on the desktop and never prompts for a click.
|
|
535
|
+
Kept in the document for the same reason as the queue card above:
|
|
536
|
+
`fp-card-status` is painted by renderer/fingerprint.js and
|
|
537
|
+
`fp-open` is the panel's own open seam, and an absent node is a
|
|
538
|
+
silent no-op in one and a thrown paint in the other.
|
|
539
|
+
|
|
540
|
+
φ(α) — the authorship key and the sealed session records.
|
|
385
541
|
The heading is the click target for the full panel, exactly like
|
|
386
542
|
the Memory card (a <button> may only hold phrasing content, so the
|
|
387
543
|
button goes inside the h2, never around it).
|
|
@@ -424,38 +580,20 @@
|
|
|
424
580
|
<div class="title" id="chat-greeting">Hello</div>
|
|
425
581
|
</div>
|
|
426
582
|
<div class="sub" id="welcome-tagline">Your intent, AI automated.</div>
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
>
|
|
442
|
-
<span class="qp-ic">🐛</span>Debug an error
|
|
443
|
-
</button>
|
|
444
|
-
<button
|
|
445
|
-
class="chat-quick-pill"
|
|
446
|
-
type="button"
|
|
447
|
-
data-quick="Compare two approaches to "
|
|
448
|
-
>
|
|
449
|
-
<span class="qp-ic">⇄</span>Weigh two options
|
|
450
|
-
</button>
|
|
451
|
-
<button
|
|
452
|
-
class="chat-quick-pill"
|
|
453
|
-
type="button"
|
|
454
|
-
data-quick="Help me plan "
|
|
455
|
-
>
|
|
456
|
-
<span class="qp-ic">⊞</span>Plan a project
|
|
457
|
-
</button>
|
|
458
|
-
</div>
|
|
583
|
+
<!-- One line, chosen from what this process actually knows about THIS
|
|
584
|
+
install — never a menu of canned topics. The five generic pills
|
|
585
|
+
that used to sit here ("Write code", "Explain a concept", …) were
|
|
586
|
+
the same list on every machine for every user: a topic menu is
|
|
587
|
+
not a welcome, it is a form to fill in. The line below is built
|
|
588
|
+
by welcomePersonalize() in app.js from the same fact-gated rules
|
|
589
|
+
as the sidebar's daily message (renderer/companion.js): an
|
|
590
|
+
update that is downloaded and waiting, a missing key with the one
|
|
591
|
+
concrete step, or memory entries this account already holds. When
|
|
592
|
+
no fact is present it rotates one true tip, seeded by the day;
|
|
593
|
+
when there is nothing to say at all it stays hidden rather than
|
|
594
|
+
guessing. `hidden` is in the markup so a render that never runs
|
|
595
|
+
shows nothing instead of an empty box. -->
|
|
596
|
+
<p class="sub welcome-personal" id="welcome-personal" hidden></p>
|
|
459
597
|
<!-- Why AEGIS Desktop, and the ONE reason to pay.
|
|
460
598
|
Phase 3 of docs/upgrade-conversion-plan.md. This block used to
|
|
461
599
|
carry four competing bullets (local, your key, does the work,
|
|
@@ -585,7 +723,14 @@
|
|
|
585
723
|
rows="2"
|
|
586
724
|
placeholder="Message AEGIS… (Enter to send, Shift+Enter for a newline)"
|
|
587
725
|
></textarea>
|
|
588
|
-
|
|
726
|
+
<!-- The composer's control row: every switch that changes what this
|
|
727
|
+
send will do lives INSIDE the composer box, on one line under the
|
|
728
|
+
input, so the whole thing reads as one object with one focus ring.
|
|
729
|
+
Before this, the discovery toggle sat in a side rail and the tool
|
|
730
|
+
mode sat in a strip underneath the form — three surfaces around
|
|
731
|
+
one text field, and the mode chip (which changes whether tools run
|
|
732
|
+
unattended) was the furthest from the thing it governs. -->
|
|
733
|
+
<div class="composer-row">
|
|
589
734
|
<label
|
|
590
735
|
class="flow-toggle"
|
|
591
736
|
for="explore-toggle"
|
|
@@ -597,26 +742,26 @@
|
|
|
597
742
|
<input type="checkbox" id="explore-toggle" />
|
|
598
743
|
<span>discovery lane</span>
|
|
599
744
|
</label>
|
|
745
|
+
<!-- Mode chip: the same switch as Settings → Tool approvals, hoisted
|
|
746
|
+
next to the input so the current mode is readable while typing
|
|
747
|
+
and one click from the other. Painted from
|
|
748
|
+
renderConfirmMode()/renderAutoModeChip() in app.js, so the two
|
|
749
|
+
cannot disagree. type="button" so it never submits the form. -->
|
|
750
|
+
<button
|
|
751
|
+
type="button"
|
|
752
|
+
id="auto-mode-toggle"
|
|
753
|
+
class="mode-chip"
|
|
754
|
+
aria-pressed="false"
|
|
755
|
+
title="Auto mode lets the agent run exec, writeFile and editFile without asking. Click to switch, or set it in Settings → Tool approvals."
|
|
756
|
+
>
|
|
757
|
+
<span class="mode-dot" aria-hidden="true">●</span>
|
|
758
|
+
<span id="auto-mode-label">ask before tools</span>
|
|
759
|
+
</button>
|
|
760
|
+
<span class="hint" id="auto-mode-hint"></span>
|
|
761
|
+
<span class="composer-spacer"></span>
|
|
600
762
|
<button type="submit" id="send">Send</button>
|
|
601
763
|
</div>
|
|
602
764
|
</form>
|
|
603
|
-
<!-- Mode strip: the same switch as Settings → Tool approvals, hoisted
|
|
604
|
-
directly under the chat bar so the current mode is readable while
|
|
605
|
-
typing and one click from the other. Both controls are painted from
|
|
606
|
-
renderConfirmMode() in app.js, so they cannot disagree. -->
|
|
607
|
-
<div class="composer-modes">
|
|
608
|
-
<button
|
|
609
|
-
type="button"
|
|
610
|
-
id="auto-mode-toggle"
|
|
611
|
-
class="mode-chip"
|
|
612
|
-
aria-pressed="false"
|
|
613
|
-
title="Auto mode lets the agent run exec, writeFile and editFile without asking. Click to switch, or set it in Settings → Tool approvals."
|
|
614
|
-
>
|
|
615
|
-
<span class="mode-dot" aria-hidden="true">●</span>
|
|
616
|
-
<span id="auto-mode-label">ask before tools</span>
|
|
617
|
-
</button>
|
|
618
|
-
<span class="hint" id="auto-mode-hint"></span>
|
|
619
|
-
</div>
|
|
620
765
|
</section>
|
|
621
766
|
</main>
|
|
622
767
|
|
|
@@ -645,7 +790,7 @@
|
|
|
645
790
|
<input
|
|
646
791
|
type="text"
|
|
647
792
|
id="memory-overlay-query"
|
|
648
|
-
placeholder="Filter by text, tag
|
|
793
|
+
placeholder="Filter by text, tag or source…"
|
|
649
794
|
autocomplete="off"
|
|
650
795
|
/>
|
|
651
796
|
<button type="submit">Filter</button>
|
|
@@ -698,6 +843,22 @@
|
|
|
698
843
|
<div class="fp-row"><span class="fp-k">key file</span><span class="fp-v" id="fp-key-path">–</span></div>
|
|
699
844
|
<div class="fp-row"><span class="fp-k">publishes to</span><span class="fp-v" id="fp-vault-target">–</span></div>
|
|
700
845
|
</div>
|
|
846
|
+
|
|
847
|
+
<!-- Where a publish/backup goes. Three named destinations, not a
|
|
848
|
+
dropdown: `aria-pressed` on the active one is the whole state, so
|
|
849
|
+
fingerprint.js's paintVaultBackend() is one loop over three buttons
|
|
850
|
+
rather than a <select> whose value can drift from main's answer. -->
|
|
851
|
+
<div class="fp-vault-backend" id="fp-vault-backend" role="group" aria-label="where records are published">
|
|
852
|
+
<button type="button" id="fp-vault-backend-drive" class="ghost-btn fp-backend-btn" aria-pressed="false">
|
|
853
|
+
Google Drive
|
|
854
|
+
</button>
|
|
855
|
+
<button type="button" id="fp-vault-backend-aegiscloud" class="ghost-btn fp-backend-btn" aria-pressed="false">
|
|
856
|
+
AE
|
|
857
|
+
</button>
|
|
858
|
+
<button type="button" id="fp-vault-backend-off" class="ghost-btn fp-backend-btn" aria-pressed="false">
|
|
859
|
+
No sync
|
|
860
|
+
</button>
|
|
861
|
+
</div>
|
|
701
862
|
<p class="fp-warn" id="fp-key-warning" hidden></p>
|
|
702
863
|
|
|
703
864
|
<div class="fp-controls">
|
|
@@ -739,7 +900,7 @@
|
|
|
739
900
|
<label
|
|
740
901
|
class="flow-toggle"
|
|
741
902
|
for="fp-auto-seal"
|
|
742
|
-
title="Seal this session as a φ(α) record once it has been quiet for 90 seconds.
|
|
903
|
+
title="Seal this session as a φ(α) record once it has been quiet for 90 seconds. On by default (this switch only turns it off). The seal happens whether or not anyone is watching, but publishing to the vault stays a separate checkbox."
|
|
743
904
|
>
|
|
744
905
|
<input type="checkbox" id="fp-auto-seal" />
|
|
745
906
|
<span id="fp-auto-seal-label">seal automatically when the session goes quiet</span>
|
|
@@ -766,6 +927,23 @@
|
|
|
766
927
|
|
|
767
928
|
<ul class="fp-vault-list" id="fp-vault-list"></ul>
|
|
768
929
|
|
|
930
|
+
<!-- Google Drive — memory backup. Unrelated to the vault above: that
|
|
931
|
+
publishes a sealed φ(α) RECORD; this backs up the account's AEGIS
|
|
932
|
+
CLOUD MEMORY to the user's own Drive through real Google OAuth
|
|
933
|
+
(desktop/lib/google-connect.js). Two buttons because connecting
|
|
934
|
+
Drive is only coherent once this machine has signed in with the
|
|
935
|
+
SAME Google identity — see that file's header comment — so
|
|
936
|
+
"Connect Drive" stays disabled (fingerprint.js paintDrive()) until
|
|
937
|
+
sign-in has actually landed. -->
|
|
938
|
+
<div class="fp-controls">
|
|
939
|
+
<div class="fp-row"><span class="fp-k">Google Drive</span><span class="fp-v" id="fp-drive-status">–</span></div>
|
|
940
|
+
<div class="fp-actions">
|
|
941
|
+
<button type="button" id="fp-drive-signin" class="ghost-btn">Sign in with Google</button>
|
|
942
|
+
<button type="button" id="fp-drive-connect" class="ghost-btn" disabled>Connect Drive</button>
|
|
943
|
+
<button type="button" id="fp-drive-sync" class="ghost-btn" disabled>Back up memory now</button>
|
|
944
|
+
</div>
|
|
945
|
+
</div>
|
|
946
|
+
|
|
769
947
|
<div class="fp-out" id="fp-out">Ready. The key is never displayed.</div>
|
|
770
948
|
</div>
|
|
771
949
|
</div>
|
|
@@ -856,6 +1034,9 @@
|
|
|
856
1034
|
</div>
|
|
857
1035
|
</div>
|
|
858
1036
|
|
|
1037
|
+
<script src="providers.js"></script>
|
|
1038
|
+
<script src="provider-toolbar.js"></script>
|
|
1039
|
+
<script src="companion.js"></script>
|
|
859
1040
|
<script src="budget.js"></script>
|
|
860
1041
|
<script src="usage.js"></script>
|
|
861
1042
|
<script src="stream-policy.js"></script>
|