aegis-desktop 0.8.28 → 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 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 **no pinnable model** in this app: its dropdown keeps the
65
- one *server default (auto)* entry, and the aegiscloud ids (`nexus-brain` and the
66
- `-smart`/`-neo` spellings the server sends as aliases of it, plus the
67
- per-provider ids it lists beside them) are omitted from the list. The lane is
68
- unchanged by that — the account key, the pooled turn, cloud memory sync and the
69
- BYOK handling fee billed to this account all still work; there is simply nothing
70
- to pin. Per-provider models are reachable through **Bring your own key** below.
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
- Note that a **BYOK turn is single-shot** — the relay takes no `tools` parameter,
112
- so the agentic tool loop below is off for these models. Use the **Aegis Cloud**
113
- class when you want file and shell access.
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
 
@@ -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
- * @returns {{messages:Array, system:string, dropped:number, estimatedTokens:number}}
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
- let used = 0;
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
 
@@ -2114,6 +2114,18 @@ function createLocalEngine({
2114
2114
  return { ok: Boolean(controller) };
2115
2115
  }
2116
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
+
2117
2129
  return {
2118
2130
  CLASSES,
2119
2131
  listClasses,
@@ -2123,6 +2135,7 @@ function createLocalEngine({
2123
2135
  respondApproval,
2124
2136
  clearSessionApprovals,
2125
2137
  settings,
2138
+ unloadLocalModels,
2126
2139
  };
2127
2140
  }
2128
2141