aegis-desktop 0.8.27 → 0.8.28
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/local/engine.js +24 -6
- package/package.json +1 -1
- package/vendor/aegis.js +9 -0
- package/vendor/platform.js +37 -0
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
|
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "aegis-desktop",
|
|
3
3
|
"productName": "AEGIS Desktop",
|
|
4
|
-
"version": "0.8.
|
|
4
|
+
"version": "0.8.28",
|
|
5
5
|
"description": "Thin Electron host for AEGIS — a local chat UI over the shared client/aegis.js transport. Ships transport + UI only; engine logic stays server-side.",
|
|
6
6
|
"author": {
|
|
7
7
|
"name": "AEGIS Code",
|
package/vendor/aegis.js
CHANGED
|
@@ -1002,6 +1002,8 @@ function createClient(opts = {}) {
|
|
|
1002
1002
|
maxTokens,
|
|
1003
1003
|
effort,
|
|
1004
1004
|
reasoningEffort,
|
|
1005
|
+
tools,
|
|
1006
|
+
toolChoice,
|
|
1005
1007
|
stream = false,
|
|
1006
1008
|
onStream,
|
|
1007
1009
|
signal,
|
|
@@ -1035,6 +1037,13 @@ function createClient(opts = {}) {
|
|
|
1035
1037
|
// failed turn, not a harmless extra key.
|
|
1036
1038
|
...(effort ? { effort } : {}),
|
|
1037
1039
|
...(reasoningEffort ? { reasoning_effort: reasoningEffort } : {}),
|
|
1040
|
+
// Forwarded only when the caller actually supplied a schema — aegis1's
|
|
1041
|
+
// byok_chat_completions (app.py) reads `tools`/`tool_choice` off the body
|
|
1042
|
+
// and passes each to the provider only `if tools:`, so an empty array
|
|
1043
|
+
// here would still be a behavioural change for a provider that treats
|
|
1044
|
+
// `tools: []` differently from the field's plain absence.
|
|
1045
|
+
...(Array.isArray(tools) && tools.length ? { tools } : {}),
|
|
1046
|
+
...(toolChoice ? { tool_choice: toolChoice } : {}),
|
|
1038
1047
|
};
|
|
1039
1048
|
if (model) body.model = model;
|
|
1040
1049
|
const headers = {
|
package/vendor/platform.js
CHANGED
|
@@ -871,6 +871,42 @@ function resolvePath(input, options = {}) {
|
|
|
871
871
|
return { ok: true, path: resolved, error: null, expandedHome };
|
|
872
872
|
}
|
|
873
873
|
|
|
874
|
+
/**
|
|
875
|
+
* The last path segment of `input` — the NAME of a path, for display.
|
|
876
|
+
*
|
|
877
|
+
* This exists because `p.split('/').pop()` was the same expression in six CLI
|
|
878
|
+
* files, and it is wrong on half the hosts this product ships to: a real win32
|
|
879
|
+
* separator is `\`, so `C:\Users\you\proj` splits into ONE element and the
|
|
880
|
+
* "name" reported is the whole path. The status line, the session list, the
|
|
881
|
+
* resume picker and the `/export` file names all showed the full path there
|
|
882
|
+
* where linux and darwin showed `proj` — a cosmetic drift, but drifts are how
|
|
883
|
+
* the tool-layer bug shipped twice, and it is the same class of assumption
|
|
884
|
+
* (`/` is the separator) that `resolvePath` above exists to refuse.
|
|
885
|
+
*
|
|
886
|
+
* BOTH separators are accepted on every platform, unlike `resolvePath` which
|
|
887
|
+
* refuses a foreign path outright. That difference is deliberate: a name is not
|
|
888
|
+
* a security decision. The value can come from a git remote, a session recorded
|
|
889
|
+
* on another host, or a model's own output, and mis-slicing it produces a
|
|
890
|
+
* shorter label, never a write to the wrong file — so refusing it would trade a
|
|
891
|
+
* cosmetic bug for a worse one (an empty status line on a machine whose only
|
|
892
|
+
* oddity is a backslash).
|
|
893
|
+
*
|
|
894
|
+
* Returns `''` for an absent or empty path so each caller keeps its own
|
|
895
|
+
* fallback (`baseName(cwd) || '~'` is the CLI's "no cwd recorded" convention);
|
|
896
|
+
* a path that is nothing but separators ("/", "C:\") names no segment, so it is
|
|
897
|
+
* empty too rather than a stray "/" or a bare drive letter.
|
|
898
|
+
*/
|
|
899
|
+
function baseName(input) {
|
|
900
|
+
if (typeof input !== 'string') return '';
|
|
901
|
+
const trimmed = input.trim().replace(/[/\\]+$/, '');
|
|
902
|
+
if (!trimmed) return '';
|
|
903
|
+
// "C:" is a drive, not a name: it is the Windows spelling of "/" (the root of
|
|
904
|
+
// the current drive) and names no segment any more than a slash does.
|
|
905
|
+
if (/^[A-Za-z]:$/.test(trimmed)) return '';
|
|
906
|
+
const cut = Math.max(trimmed.lastIndexOf('/'), trimmed.lastIndexOf('\\'));
|
|
907
|
+
return cut === -1 ? trimmed : trimmed.slice(cut + 1);
|
|
908
|
+
}
|
|
909
|
+
|
|
874
910
|
const api = {
|
|
875
911
|
IS_WIN32,
|
|
876
912
|
resolveExecutable,
|
|
@@ -881,6 +917,7 @@ const api = {
|
|
|
881
917
|
homeDir,
|
|
882
918
|
desktopDir,
|
|
883
919
|
resolvePath,
|
|
920
|
+
baseName,
|
|
884
921
|
hasPosixModes,
|
|
885
922
|
appLocale,
|
|
886
923
|
languageName,
|