aegis-desktop 0.8.27 → 0.8.29
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +33 -10
- package/lib/local/context.js +13 -2
- package/lib/local/engine.js +37 -6
- package/lib/local/local.js +1410 -46
- package/lib/settings.js +141 -14
- package/lib/sync/persist-gate.js +35 -10
- package/main.js +201 -13
- package/package.json +2 -1
- package/renderer/app.js +24 -8
- package/vendor/aegis.js +9 -0
- package/vendor/platform.js +44 -0
- package/vendor/prefs.js +318 -0
- package/vendor/topup.js +294 -0
- package/vendor/update.js +141 -3
package/README.md
CHANGED
|
@@ -61,13 +61,19 @@ with context intact:
|
|
|
61
61
|
| **Bring your own key** | your provider key, relayed by AEGIS — see below | main process | yes — flat AEGIS handling fee |
|
|
62
62
|
| **Local** | a model on your own machine or network — your Ollama daemon at `localhost:11434` by default | n/a — no key | no — free, no AEGIS account needed |
|
|
63
63
|
|
|
64
|
-
The Cloud class offers **
|
|
65
|
-
one
|
|
66
|
-
`-smart`/`-neo` spellings the server sends as aliases of it
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
64
|
+
The Cloud class offers **everything the server advertises**: the pooled brain
|
|
65
|
+
first, as one row (`Nexus` — the `nexus-brain`/`aegis-brain` and the
|
|
66
|
+
`-smart`/`-neo` spellings the server sends as aliases of it are folded into that
|
|
67
|
+
one row, because they all name the same route), then every per-provider id the
|
|
68
|
+
same catalogue lists, so `deepseek`, `anthropic`, `groq`, `openai` … can be
|
|
69
|
+
pinned directly. Both hosts build that list from one shared rule
|
|
70
|
+
(`client/brain-catalog.js`, `offerableCatalog`), so the GUI and the terminal
|
|
71
|
+
cannot show one account two different lists. A pinned per-provider id is still a
|
|
72
|
+
**pooled** turn: your account key pays the pool's margin, and the plan and
|
|
73
|
+
balance gates on `/api/v1/chat/completions` apply to it exactly as they do to
|
|
74
|
+
the brain row. The same models are also reachable through **Bring your own
|
|
75
|
+
key** below — your provider key, and a flat handling fee instead of the pool's
|
|
76
|
+
margin.
|
|
71
77
|
|
|
72
78
|
Get a free AEGIS key at **https://aegiscloud.org** for the Cloud and BYOK
|
|
73
79
|
classes. The generic OpenAI-/Anthropic-compatible direct-dial lanes that used
|
|
@@ -108,9 +114,26 @@ traffic — BYOK stays the cheaper lane, it just is not the free one.
|
|
|
108
114
|
handling fee has to be billed somewhere. With no AEGIS key connected the class
|
|
109
115
|
shows every model but says exactly that, rather than failing opaquely.
|
|
110
116
|
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
117
|
+
**And the relay decides whether your bank has to be funded.** The route
|
|
118
|
+
publishes its own policy on `GET /api/v1/byok/providers` (`fee.gate_mode`,
|
|
119
|
+
`fee.require_account`, `fee.require_balance`) and the app reads it there rather
|
|
120
|
+
than assuming one: in the `balance` mode the live deployment runs, a BYOK turn
|
|
121
|
+
needs an account **with a balance**, and a zero bank answers `402 Insufficient
|
|
122
|
+
balance. Top up to continue using BYOK.` — your provider key is still valid, the
|
|
123
|
+
AEGIS handling fee is what ran out (see the top-up surface). In the softer
|
|
124
|
+
`owner` mode a named account is enough and an unfunded fee is recorded as owed.
|
|
125
|
+
An anonymous caller (no AEGIS key at all) is refused in every enforcing mode,
|
|
126
|
+
which is the released clients' own gate too — this class does not serve a turn
|
|
127
|
+
nobody can be billed for.
|
|
128
|
+
|
|
129
|
+
**The agentic tool loop works here too.** The relay's BYOK route accepts the
|
|
130
|
+
OpenAI-shaped `tools`/`tool_choice` and translates them to each provider's own
|
|
131
|
+
wire format (Anthropic-backed callers get real tool calls), so file, shell and
|
|
132
|
+
search access below are available on this lane — the engine sends the same tool
|
|
133
|
+
schema it sends on the pooled lane. What the class does *not* get is the cloud
|
|
134
|
+
drain in the **autonomous queue**, which is `Aegis Cloud`-only (see
|
|
135
|
+
`desktop/lib/local/autonomous.js`) — that is the fan-out Pro buys, not the tool
|
|
136
|
+
loop.
|
|
114
137
|
|
|
115
138
|
## Screenshots
|
|
116
139
|
|
package/lib/local/context.js
CHANGED
|
@@ -40,17 +40,27 @@ function messageTokens(message) {
|
|
|
40
40
|
* @param {string} [opts.system='']
|
|
41
41
|
* @param {number} [opts.budgetTokens=8192] total tokens allowed on the wire
|
|
42
42
|
* @param {number} [opts.reserveTokens=0] tokens reserved for the reply
|
|
43
|
-
* @
|
|
43
|
+
* @param {number} [opts.fixedTokens=0] wire cost that is NOT a message and
|
|
44
|
+
* cannot be dropped (the tool schemas), counted before any turn is kept
|
|
45
|
+
* @returns {{messages:Array, system:string, dropped:number, estimatedTokens:number, fixedTokens:number}}
|
|
44
46
|
*/
|
|
45
47
|
function trimContext({
|
|
46
48
|
messages = [],
|
|
47
49
|
system = '',
|
|
48
50
|
budgetTokens = 8192,
|
|
49
51
|
reserveTokens = 0,
|
|
52
|
+
fixedTokens = 0,
|
|
50
53
|
} = {}) {
|
|
51
54
|
const limit = Math.max(0, budgetTokens - reserveTokens);
|
|
52
55
|
const kept = [];
|
|
53
|
-
|
|
56
|
+
// Tool schemas go on the wire every round and cannot be trimmed away, so they
|
|
57
|
+
// are charged against the budget FIRST. Without this the trimmer believed a
|
|
58
|
+
// 4096-token window had 4096 tokens for history when the schemas had already
|
|
59
|
+
// spent ~1971 of them (measured: 10 schemas, 7882 chars) — and the daemon,
|
|
60
|
+
// run with `--context-shift`, discarded the SYSTEM row to make room instead
|
|
61
|
+
// of erroring. Counting them here is what keeps the system row alive.
|
|
62
|
+
const fixed = Math.max(0, Number(fixedTokens) || 0);
|
|
63
|
+
let used = fixed;
|
|
54
64
|
|
|
55
65
|
if (system) used += MESSAGE_OVERHEAD + estimateTokens(system);
|
|
56
66
|
|
|
@@ -69,6 +79,7 @@ function trimContext({
|
|
|
69
79
|
system,
|
|
70
80
|
dropped: messages.length - kept.length,
|
|
71
81
|
estimatedTokens: used,
|
|
82
|
+
fixedTokens: fixed,
|
|
72
83
|
};
|
|
73
84
|
}
|
|
74
85
|
|
package/lib/local/engine.js
CHANGED
|
@@ -1392,6 +1392,17 @@ function createLocalEngine({
|
|
|
1392
1392
|
system: opts.system,
|
|
1393
1393
|
messages: opts.messages,
|
|
1394
1394
|
maxTokens: opts.maxTokens,
|
|
1395
|
+
// aegis1's byok_chat_completions has read and forwarded `tools`/
|
|
1396
|
+
// `tool_choice` since 926069b (2026-09-10) — this call simply never
|
|
1397
|
+
// sent them, so every BYOK turn (DeepSeek, Anthropic, OpenAI alike)
|
|
1398
|
+
// ran with no write/exec/editFile schema at all. A model told in its
|
|
1399
|
+
// system prompt that it "writes and edits files" but never given the
|
|
1400
|
+
// JSON shape to do so improvises a DeepSeek-native DSML call from
|
|
1401
|
+
// prose alone when it's confident, and falls back to prose ADVICE
|
|
1402
|
+
// (a shell script to run by hand) when it isn't — which is exactly
|
|
1403
|
+
// the bug this line closes.
|
|
1404
|
+
tools: opts.tools,
|
|
1405
|
+
toolChoice: opts.toolChoice,
|
|
1395
1406
|
// Native effort knob, field and level both from the dialect table.
|
|
1396
1407
|
// `bareModel` is already provider-less, so it matches the dialect
|
|
1397
1408
|
// regexes without further stripping. Only a dialect whose field is
|
|
@@ -1530,12 +1541,19 @@ function createLocalEngine({
|
|
|
1530
1541
|
const signal = controller.signal;
|
|
1531
1542
|
|
|
1532
1543
|
// A caller can opt out of the agent loop entirely (`tools: false`) and get
|
|
1533
|
-
// the old single-shot turn back. byok
|
|
1534
|
-
//
|
|
1535
|
-
//
|
|
1536
|
-
//
|
|
1537
|
-
//
|
|
1538
|
-
|
|
1544
|
+
// the old single-shot turn back. byok used to be forced into that same
|
|
1545
|
+
// single-shot shape unconditionally: aegis1's byok relay had no
|
|
1546
|
+
// tools/tool_choice parameter at all, so sending schemas would build a
|
|
1547
|
+
// prompt promising tool access the transport silently dropped — the model
|
|
1548
|
+
// would reason about exec/readFile and never see a result. That stopped
|
|
1549
|
+
// being true at aegis1 926069b (2026-09-10, byok_chat_completions reads
|
|
1550
|
+
// and forwards both), but this carve-out was never revisited, so every
|
|
1551
|
+
// BYOK turn since kept running with no tool schema at all regardless —
|
|
1552
|
+
// the model told in its own system prompt that it writes and edits files,
|
|
1553
|
+
// and never given the JSON shape to do so. Reported live: asked to save a
|
|
1554
|
+
// file to the Desktop, DeepSeek had no write tool to call and answered
|
|
1555
|
+
// with a PowerShell script for the user to run by hand instead.
|
|
1556
|
+
const toolsEnabled = !(payload && payload.tools === false);
|
|
1539
1557
|
const toolSchemas = toolsEnabled ? T.toolsFor({ includeSubagent: depth < MAX_SUBAGENT_DEPTH }) : [];
|
|
1540
1558
|
const toolChoice = (payload && payload.toolChoice) || null;
|
|
1541
1559
|
|
|
@@ -2096,6 +2114,18 @@ function createLocalEngine({
|
|
|
2096
2114
|
return { ok: Boolean(controller) };
|
|
2097
2115
|
}
|
|
2098
2116
|
|
|
2117
|
+
/**
|
|
2118
|
+
* Unload every local model this engine warmed, across every daemon it
|
|
2119
|
+
* talked to (local.js `unloadAll`). The desktop host calls this on its quit
|
|
2120
|
+
* path so closing the app also closes the model — the local lane bills
|
|
2121
|
+
* nothing precisely because it is YOUR hardware, and hardware left running
|
|
2122
|
+
* a model nobody is using anymore after the app exits is the one cost that
|
|
2123
|
+
* lane was supposed to never have.
|
|
2124
|
+
*/
|
|
2125
|
+
function unloadLocalModels() {
|
|
2126
|
+
return localT.unloadAll();
|
|
2127
|
+
}
|
|
2128
|
+
|
|
2099
2129
|
return {
|
|
2100
2130
|
CLASSES,
|
|
2101
2131
|
listClasses,
|
|
@@ -2105,6 +2135,7 @@ function createLocalEngine({
|
|
|
2105
2135
|
respondApproval,
|
|
2106
2136
|
clearSessionApprovals,
|
|
2107
2137
|
settings,
|
|
2138
|
+
unloadLocalModels,
|
|
2108
2139
|
};
|
|
2109
2140
|
}
|
|
2110
2141
|
|