aegiscode 6.3.1 → 6.3.2
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/package.json +1 -1
- package/src/app.js +15 -1
- package/src/chatflow.js +7 -4
- package/src/commands.js +20 -8
- package/src/config.js +6 -1
- package/src/overlays.js +18 -5
- package/src/panels.js +3 -1
- package/vendor/client/aegis.js +16 -1
- package/vendor/desktop/lib/local/engine.js +12 -7
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "aegiscode",
|
|
3
3
|
"productName": "AEGIS Code",
|
|
4
|
-
"version": "6.3.
|
|
4
|
+
"version": "6.3.2",
|
|
5
5
|
"description": "aegiscode — the command-line version of AEGIS Desktop. The shared tool surface in your shell, over the same thin transport and tool registry as the MCP plugin and the desktop app. Ships transport + UI only; no brain.",
|
|
6
6
|
"author": {
|
|
7
7
|
"name": "AEGIS Code",
|
package/src/app.js
CHANGED
|
@@ -87,7 +87,11 @@ function createApp(options = {}) {
|
|
|
87
87
|
const commandCtx = {
|
|
88
88
|
light: opts.light,
|
|
89
89
|
model: opts.model,
|
|
90
|
-
|
|
90
|
+
// `null` = "auto": send no effort, so the server sizes the turn from the
|
|
91
|
+
// ask. This is the *budget* control on the pooled class (aegis1 sizes the
|
|
92
|
+
// token ladder from it), which is why it is not defaulted to a rung here —
|
|
93
|
+
// pinning one would make every turn cost what that rung grants.
|
|
94
|
+
effort: null,
|
|
91
95
|
thinking: false,
|
|
92
96
|
themeIndex: opts.light ? 2 : 1,
|
|
93
97
|
vim: false,
|
|
@@ -371,6 +375,14 @@ function createApp(options = {}) {
|
|
|
371
375
|
model: commandCtx.model || undefined,
|
|
372
376
|
system: opts.system,
|
|
373
377
|
maxTokens: opts.maxTokens,
|
|
378
|
+
// `effort` is the pooled budget control, and it travels on every turn
|
|
379
|
+
// — this host only ever runs the 'aegis' class, whose calls are sized
|
|
380
|
+
// server-side from it (aegis1 services/pool_brain.py). It used to be
|
|
381
|
+
// held in the session state and rendered in the status line without
|
|
382
|
+
// ever reaching the wire, so /effort changed the display and nothing
|
|
383
|
+
// about what the turn cost. `null` (auto) is omitted so the server
|
|
384
|
+
// infers the rung from the ask.
|
|
385
|
+
effort: commandCtx.effort || undefined,
|
|
374
386
|
// false asks the engine for the buffered (non-stream) wire form, so
|
|
375
387
|
// `--no-stream` and piped runs get a single body rather than SSE.
|
|
376
388
|
stream: commandCtx.stream !== false,
|
|
@@ -1073,6 +1085,8 @@ function createApp(options = {}) {
|
|
|
1073
1085
|
return;
|
|
1074
1086
|
}
|
|
1075
1087
|
if (!opts.model && cfg.model) commandCtx.model = cfg.model;
|
|
1088
|
+
// `null` in the config is "auto" and is deliberately not assigned over the
|
|
1089
|
+
// default — there is nothing to restore, because nothing is pinned.
|
|
1076
1090
|
if (cfg.effort) commandCtx.effort = cfg.effort;
|
|
1077
1091
|
if (typeof cfg.vim === 'boolean') commandCtx.vim = cfg.vim;
|
|
1078
1092
|
if (cfg.lastRecap) commandCtx.lastRecap = cfg.lastRecap;
|
package/src/chatflow.js
CHANGED
|
@@ -164,7 +164,7 @@ const separatorLine = (cols, ctx) => [span(themeOf(ctx).dim, '─'.repeat(cols))
|
|
|
164
164
|
/** The idle bottom-left line: which effort level the next turn runs at. */
|
|
165
165
|
function effortLine(ctx, cols) {
|
|
166
166
|
const t = themeOf(ctx);
|
|
167
|
-
const txt = `● ${ctx.effort || '
|
|
167
|
+
const txt = `● ${ctx.effort || 'auto'} · /effort`;
|
|
168
168
|
const pad = ' '.repeat(Math.max(0, cols - [...txt].length - 4));
|
|
169
169
|
return [span(t.gray, pad + txt)];
|
|
170
170
|
}
|
|
@@ -1111,12 +1111,15 @@ async function runSession(host) {
|
|
|
1111
1111
|
return;
|
|
1112
1112
|
}
|
|
1113
1113
|
if (type === 'effort') {
|
|
1114
|
-
|
|
1115
|
-
|
|
1114
|
+
// The picker's own order, not a second copy of the level names: this
|
|
1115
|
+
// used to re-declare ['low','medium','high'] here while the picker drew
|
|
1116
|
+
// its own table, so the row a user picked and the value it stored were
|
|
1117
|
+
// resolved through different lists.
|
|
1118
|
+
const chosen = overlays.EFFORT_VALUES[overlay.sel || 0];
|
|
1116
1119
|
overlay = null;
|
|
1117
1120
|
ctx.effort = chosen;
|
|
1118
1121
|
host.updateConfig({ effort: chosen });
|
|
1119
|
-
note(`effort: ${chosen}`);
|
|
1122
|
+
note(`effort: ${chosen || 'auto'}`);
|
|
1120
1123
|
render();
|
|
1121
1124
|
return;
|
|
1122
1125
|
}
|
package/src/commands.js
CHANGED
|
@@ -46,6 +46,7 @@ const { execSync } = require('node:child_process');
|
|
|
46
46
|
const { span } = require('./screen.js');
|
|
47
47
|
const { C, BOLD, BOLD_OFF } = require('./theme.js');
|
|
48
48
|
const panels = require('./panels.js');
|
|
49
|
+
const overlays = require('./overlays.js');
|
|
49
50
|
const {
|
|
50
51
|
updateConfig, loadConfig, loadPermissions, savePermissions, addPermissionRule,
|
|
51
52
|
DEFAULT_PERMISSIONS, permissionsPath, configPath,
|
|
@@ -93,7 +94,13 @@ const CATEGORIES = [
|
|
|
93
94
|
|
|
94
95
|
const categoryLabel = (id) => (CATEGORIES.find((cat) => cat.id === id) || {}).label || id;
|
|
95
96
|
|
|
96
|
-
|
|
97
|
+
// One table, shared with the picker that draws it. The two used to be separate
|
|
98
|
+
// lists — this one a string array, chatflow.js's own copy of the same three
|
|
99
|
+
// strings, and overlays.js's label/note rows — so adding a level meant editing
|
|
100
|
+
// three places, and the index a picker row reported was mapped back to a value
|
|
101
|
+
// through whichever list happened to be in scope. EFFORT_VALUES is the picker's
|
|
102
|
+
// own order, `null` first for "auto".
|
|
103
|
+
const EFFORT_LEVELS = overlays.EFFORT_VALUES;
|
|
97
104
|
|
|
98
105
|
// ── Small handler helpers ─────────────────────────────────────────────────────
|
|
99
106
|
|
|
@@ -348,12 +355,12 @@ const COMMANDS = [
|
|
|
348
355
|
},
|
|
349
356
|
},
|
|
350
357
|
{
|
|
351
|
-
name: 'effort', args: ['level'], hint: '[low|medium|high]', category: 'model',
|
|
352
|
-
desc: 'Set
|
|
358
|
+
name: 'effort', args: ['level'], hint: '[auto|low|medium|high]', category: 'model',
|
|
359
|
+
desc: 'Set the token budget the pool sizes each turn from',
|
|
353
360
|
handler: async (c, args) => {
|
|
354
361
|
const level = (args.level || '').toLowerCase();
|
|
355
|
-
if (level && !EFFORT_LEVELS.includes(level)) {
|
|
356
|
-
note(c, `Unknown effort level "${args.level}". Use ${EFFORT_LEVELS.join(', ')}.`);
|
|
362
|
+
if (level && level !== 'auto' && !EFFORT_LEVELS.includes(level)) {
|
|
363
|
+
note(c, `Unknown effort level "${args.level}". Use auto, ${EFFORT_LEVELS.filter(Boolean).join(', ')}.`);
|
|
357
364
|
c.render();
|
|
358
365
|
return true;
|
|
359
366
|
}
|
|
@@ -361,9 +368,14 @@ const COMMANDS = [
|
|
|
361
368
|
c.openOverlay({ type: 'effort', sel: Math.max(0, EFFORT_LEVELS.indexOf(c.ctx.effort)) });
|
|
362
369
|
return true;
|
|
363
370
|
}
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
371
|
+
// 'auto' is the absence of a pin, not a fourth rung: it is stored as null
|
|
372
|
+
// so nothing is sent and the server sizes the turn from the ask.
|
|
373
|
+
const pinned = level === 'auto' ? null : level;
|
|
374
|
+
c.ctx.effort = pinned;
|
|
375
|
+
c.saveConfig({ effort: pinned });
|
|
376
|
+
note(c, pinned
|
|
377
|
+
? `Effort level: ${pinned} — the pool sizes this turn's token budget from it`
|
|
378
|
+
: 'Effort level: auto — the pool sizes each turn from the ask');
|
|
367
379
|
c.render();
|
|
368
380
|
return true;
|
|
369
381
|
},
|
package/src/config.js
CHANGED
|
@@ -59,7 +59,12 @@ const DEFAULT_CONFIG = {
|
|
|
59
59
|
// aegiscode- name so /model add/remove/switch stay compatible both ways.
|
|
60
60
|
models: null,
|
|
61
61
|
currentModelId: null,
|
|
62
|
-
effort:
|
|
62
|
+
// `null` = no effort pinned, i.e. "auto": each turn is sized by the server
|
|
63
|
+
// from the ask itself (aegis1 services/pool_brain.py estimate_effort). The
|
|
64
|
+
// old 'high' was a *pin* on the top rung of the budget ladder — the most the
|
|
65
|
+
// server can grant — so the CLI's default turn was the most expensive one it
|
|
66
|
+
// could make, and /effort could only ever move it down.
|
|
67
|
+
effort: null,
|
|
63
68
|
vim: false,
|
|
64
69
|
lastCwd: '',
|
|
65
70
|
};
|
package/src/overlays.js
CHANGED
|
@@ -177,27 +177,38 @@ function renderModelPicker(models, sel = 0, width = 80, height = 24, current = n
|
|
|
177
177
|
// ── Effort picker overlay (/effort) ──────────────────────────────────────────
|
|
178
178
|
|
|
179
179
|
const EFFORT_LEVELS = [
|
|
180
|
-
{ label: '
|
|
180
|
+
{ label: 'Auto', value: null, note: 'Sized per turn from the ask (default)' },
|
|
181
|
+
{ label: 'Low', note: 'Fastest, cheapest budget' },
|
|
181
182
|
{ label: 'Medium', note: 'Balanced' },
|
|
182
|
-
{ label: 'High', note: 'Highest
|
|
183
|
+
{ label: 'High', note: 'Highest budget, slowest' },
|
|
183
184
|
];
|
|
184
185
|
|
|
186
|
+
// The picker's order reduced to the values a selection means, so the row a user
|
|
187
|
+
// picks and the value the session stores can never come from two different
|
|
188
|
+
// lists (commands.js validates against this, chatflow.js resolves the overlay
|
|
189
|
+
// through it). `null` is "auto" — no rung pinned.
|
|
190
|
+
const EFFORT_VALUES = EFFORT_LEVELS.map((lv) => (lv.value === undefined ? lv.label.toLowerCase() : lv.value));
|
|
191
|
+
|
|
185
192
|
/**
|
|
186
193
|
* @param {number} sel selected index
|
|
187
194
|
* @param {number} width
|
|
188
|
-
* @param {string|null} current the currently-selected effort level
|
|
195
|
+
* @param {string|null} current the currently-selected effort level (null = auto)
|
|
189
196
|
* @param {Array} [levels] override the level table
|
|
190
197
|
*/
|
|
191
198
|
function renderEffortPicker(sel = 0, width = 80, current = null, levels = EFFORT_LEVELS) {
|
|
192
199
|
const lines = [];
|
|
193
200
|
lines.push(blank(width));
|
|
194
201
|
lines.push([span('', ' '), span(C.white + BOLD, 'Select effort'), span(BOLD_OFF, '')]);
|
|
195
|
-
lines.push([span('', ' '), span(C.gray, '
|
|
202
|
+
lines.push([span('', ' '), span(C.gray, 'Sets the token budget the pool sizes each turn from.')]);
|
|
196
203
|
lines.push(blank(width));
|
|
197
204
|
for (let i = 0; i < levels.length; i++) {
|
|
198
205
|
const lv = levels[i];
|
|
199
206
|
const active = i === sel;
|
|
200
|
-
|
|
207
|
+
// `current == null` is auto, which is the first row's own value — matching
|
|
208
|
+
// on the label alone would never mark the default as the current choice.
|
|
209
|
+
const cur = current == null
|
|
210
|
+
? lv.value === null
|
|
211
|
+
: String(current).toLowerCase() === String(lv.label).toLowerCase();
|
|
201
212
|
const left = active ? span(C.lavender, GLYPH.cursor) : span('', ' ');
|
|
202
213
|
const nameSpan = active ? span(C.lavender, lv.label) : span(C.white, lv.label);
|
|
203
214
|
const mark = cur ? span(C.green, ' ' + GLYPH.check) : span('', '');
|
|
@@ -283,4 +294,6 @@ module.exports = {
|
|
|
283
294
|
renderModelPicker,
|
|
284
295
|
renderEffortPicker,
|
|
285
296
|
renderResumeList,
|
|
297
|
+
EFFORT_LEVELS,
|
|
298
|
+
EFFORT_VALUES,
|
|
286
299
|
};
|
package/src/panels.js
CHANGED
|
@@ -202,7 +202,9 @@ function buildStatus(state, ctx = {}) {
|
|
|
202
202
|
add('Working directory', s.cwd);
|
|
203
203
|
add('Home', s.home);
|
|
204
204
|
add('Model', s.model);
|
|
205
|
-
|
|
205
|
+
// `null` = auto: the pool sizes each turn from the ask, so the row says so
|
|
206
|
+
// rather than vanishing (add() skips empty values).
|
|
207
|
+
add('Effort', s.effort || 'auto');
|
|
206
208
|
if (s.thinking != null) add('Thinking', s.thinking ? 'on' : 'off');
|
|
207
209
|
if (s.stream != null) add('Streaming', s.stream === false ? 'off' : 'on');
|
|
208
210
|
if (s.vim != null) add('Vim keymap', s.vim ? 'on' : 'off');
|
package/vendor/client/aegis.js
CHANGED
|
@@ -278,6 +278,13 @@ function createClient(opts = {}) {
|
|
|
278
278
|
* so a reasoning-only stream still counts as unanswered.
|
|
279
279
|
* - `idleTimeoutMs` override the stalled-stream watchdog for this call
|
|
280
280
|
* (a worker fan-out is legitimately silent between passes)
|
|
281
|
+
* - `maxTokens` output ceiling. Omitted from the body when absent so the
|
|
282
|
+
* server's own budget applies — on a pooled request that is
|
|
283
|
+
* the `effort` ladder, and a number sent from here caps it.
|
|
284
|
+
* - `effort` / `workers` pooled-brain budget rung and fan-out size.
|
|
285
|
+
* Forwarded only inside `extra`, as the server reads them off
|
|
286
|
+
* the body (aegis1 services/pool_brain.py parse_brain_request)
|
|
287
|
+
* and sizes the budget from them.
|
|
281
288
|
*/
|
|
282
289
|
async function chatCompletion({
|
|
283
290
|
prompt,
|
|
@@ -298,9 +305,17 @@ function createClient(opts = {}) {
|
|
|
298
305
|
// model the server picks its default (no client-invented tier id). `mode`
|
|
299
306
|
// is a legacy server-side shorthand — forwarded verbatim only when the
|
|
300
307
|
// caller supplies it, never defaulted, never built into a model id.
|
|
308
|
+
//
|
|
309
|
+
// `max_tokens` is omitted entirely when the caller states none, rather than
|
|
310
|
+
// defaulted here. The server has its own budget ladder (`mode`/`effort` on
|
|
311
|
+
// the pooled path) and treats a body max_tokens as a *ceiling* over it, so
|
|
312
|
+
// an invented 4096 was not a harmless default: it capped every pass of a
|
|
313
|
+
// pooled-brain fan-out at 4096 and overrode the ladder the caller's effort
|
|
314
|
+
// selected. Omitting it is the documented way to say "server default" (see
|
|
315
|
+
// mcp/tools.js's max_tokens description).
|
|
301
316
|
const body = {
|
|
302
317
|
messages: buildMessages(messages, system, prompt),
|
|
303
|
-
max_tokens: maxTokens
|
|
318
|
+
...(maxTokens ? { max_tokens: maxTokens } : {}),
|
|
304
319
|
...(extra || {}),
|
|
305
320
|
};
|
|
306
321
|
if (model) body.model = model;
|
|
@@ -584,13 +584,18 @@ function createLocalEngine({ aegis, settings, ollama, providers, tools, promptBu
|
|
|
584
584
|
// model as the fan-out, one sample instead of four — otherwise
|
|
585
585
|
// dropping the fan-out would have quietly changed the model too.
|
|
586
586
|
...(brainFlag === false ? { mode: 'brain' } : {}),
|
|
587
|
-
//
|
|
588
|
-
//
|
|
589
|
-
//
|
|
590
|
-
//
|
|
591
|
-
//
|
|
592
|
-
//
|
|
593
|
-
|
|
587
|
+
// `effort` is the budget rung, and it is sent whenever the caller has
|
|
588
|
+
// one — not only alongside a fan-out. The fan-out is triggered by the
|
|
589
|
+
// model id (this class sends ``nexus-brain``), so a caller that never
|
|
590
|
+
// ticked "work autonomously" still ran a pooled call and had no way to
|
|
591
|
+
// say how big it should be: the server fell through to its own default
|
|
592
|
+
// rung. That is how a one-word prompt came to reserve the top of the
|
|
593
|
+
// ladder. A single-pass pass carries it too — inert on that path, but
|
|
594
|
+
// it keeps the request honest about what it asked for.
|
|
595
|
+
...(opts.effort ? { effort: opts.effort } : {}),
|
|
596
|
+
// Only meaningful with a running fan-out — aegis1 services/pool_brain.py
|
|
597
|
+
// parse_brain_request reads `workers` straight off the body and clamps
|
|
598
|
+
// it itself (MAX_WORKERS), so no client-side validation here.
|
|
594
599
|
...(brainFlag === true && opts.workers ? { workers: opts.workers } : {}),
|
|
595
600
|
// The pool forwards `tools` to the provider and returns tool_calls
|
|
596
601
|
// (aegis1 app.py:7765 → provider, pool_brain synthesis keeps them).
|