pi-jarvis 1.2.3 → 1.3.1
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/AGENTS.md +8 -2
- package/README.md +45 -18
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +277 -8
- package/dist/index.js.map +1 -1
- package/dist/jarvis-config.d.ts +12 -0
- package/dist/jarvis-config.d.ts.map +1 -1
- package/dist/jarvis-config.js +59 -2
- package/dist/jarvis-config.js.map +1 -1
- package/dist/main-context.js +65 -3
- package/dist/main-context.js.map +1 -1
- package/dist/overlay.js +1 -1
- package/dist/overlay.js.map +1 -1
- package/dist/side-session.d.ts +9 -0
- package/dist/side-session.d.ts.map +1 -1
- package/dist/side-session.js +184 -2
- package/dist/side-session.js.map +1 -1
- package/package.json +3 -2
package/AGENTS.md
CHANGED
|
@@ -4,18 +4,24 @@
|
|
|
4
4
|
- `pi-jarvis` is a Pi extension that opens a `/jarvis` side-conversation overlay.
|
|
5
5
|
- Core runtime files: `index.ts`, `side-session.ts`, `overlay.ts`, `jarvis-config.ts`, `session-ref.ts`.
|
|
6
6
|
|
|
7
|
-
## Current `/jarvis-model` Behavior
|
|
7
|
+
## Current `/jarvis-model` and `/jarvis-thinking` Behavior
|
|
8
8
|
- `/jarvis-model` with no request opens a model picker when Pi has a UI.
|
|
9
9
|
- `/jarvis-model <provider/model>` writes a project-scoped override to `.pi/jarvis.json`.
|
|
10
10
|
- `/jarvis-model --global <provider/model>` writes the default global override to the active Pi agent directory's `extensions/pi-jarvis.json` (by default `~/.pi/agent/extensions/pi-jarvis.json`).
|
|
11
11
|
- `/jarvis-model [--project|--global] follow-main` stores a scoped `follow-main` override.
|
|
12
12
|
- `/jarvis-model [--project|--global] clear` removes that scope's override so fallback applies.
|
|
13
|
-
-
|
|
13
|
+
- `/jarvis-thinking [--project|--global] auto|follow-main|off|minimal|low|medium|high|xhigh` stores a scoped thinking override without changing the main session thinking level.
|
|
14
|
+
- `/jarvis-thinking [--project|--global] clear` removes that scope's thinking override so fallback applies.
|
|
15
|
+
- Resolution order is: project config, then global config, then built-in defaults (`follow-main` for model, `auto` for thinking).
|
|
16
|
+
- `auto` thinking follows main only when `/jarvis` follows the main model; pinned `/jarvis` models default to thinking `off`.
|
|
17
|
+
- `follow-main` thinking follows the main thinking level regardless of model selection, except xAI `/jarvis` models force thinking `off`.
|
|
18
|
+
- `/compact`, `/tree`, and `/new` entered inside `/jarvis` operate on the isolated `/jarvis` side-session, not the main Pi session.
|
|
14
19
|
- `Note main` and `Redirect` stay disabled when the active `/jarvis` model is incompatible with bridge tools.
|
|
15
20
|
|
|
16
21
|
## Validation
|
|
17
22
|
- Run `npm test` for full validation.
|
|
18
23
|
- Run `npm run build` before release packaging.
|
|
24
|
+
- Run `npm run verify:release` to validate npm pack payload and release metadata expectations.
|
|
19
25
|
|
|
20
26
|
## Docs To Keep In Sync
|
|
21
27
|
- `README.md`
|
package/README.md
CHANGED
|
@@ -13,6 +13,8 @@
|
|
|
13
13
|
[](https://github.com/fluxgear/pi-jarvis)
|
|
14
14
|
[](./package.json)
|
|
15
15
|
|
|
16
|
+
<p><strong>Current version:</strong> 1.3.1</p>
|
|
17
|
+
|
|
16
18
|
<p>
|
|
17
19
|
<strong>Persistent side session</strong> ·
|
|
18
20
|
<strong>Live main-session awareness</strong> ·
|
|
@@ -79,15 +81,15 @@ flowchart LR
|
|
|
79
81
|
|
|
80
82
|
```mermaid
|
|
81
83
|
flowchart TD
|
|
82
|
-
A[Main session keeps moving] --> B[/jarvis opens in overlay]
|
|
83
|
-
B --> C[Jarvis sees current main-session context]
|
|
84
|
-
C --> D{What do you need?}
|
|
85
|
-
D -->|Status / summary / analysis| E[Jarvis handles the side task]
|
|
86
|
-
D -->|Repo inspection| F[Enable Repo tools]
|
|
87
|
-
D -->|Influence the main lane| G[Enable Note main or Redirect]
|
|
88
|
-
G --> H{Redirect?}
|
|
89
|
-
H -->|Yes| I[Per-send confirmation]
|
|
90
|
-
H -->|No| J[Quiet follow-up note]
|
|
84
|
+
A["Main session keeps moving"] --> B["/jarvis opens in overlay"]
|
|
85
|
+
B --> C["Jarvis sees current main-session context"]
|
|
86
|
+
C --> D{"What do you need?"}
|
|
87
|
+
D -->|Status / summary / analysis| E["Jarvis handles the side task"]
|
|
88
|
+
D -->|Repo inspection| F["Enable Repo tools"]
|
|
89
|
+
D -->|Influence the main lane| G["Enable Note main or Redirect"]
|
|
90
|
+
G --> H{"Redirect?"}
|
|
91
|
+
H -->|Yes| I["Per-send confirmation"]
|
|
92
|
+
H -->|No| J["Quiet follow-up note"]
|
|
91
93
|
```
|
|
92
94
|
|
|
93
95
|
---
|
|
@@ -165,26 +167,49 @@ Restores the chosen scope to `follow-main`. A project-scoped `follow-main` overr
|
|
|
165
167
|
### `/jarvis-model [--project|--global] clear`
|
|
166
168
|
Removes the selected scope so `/jarvis` falls back through the remaining config layers to the built-in default.
|
|
167
169
|
|
|
170
|
+
### `/jarvis-thinking [--project|--global] auto|follow-main|off|minimal|low|medium|high|xhigh`
|
|
171
|
+
Sets the thinking level used by `/jarvis` without changing the main session thinking level. A plain `/jarvis-thinking <level>` writes a **project-local** override to `.pi/jarvis.json`.
|
|
172
|
+
|
|
173
|
+
- `auto` preserves the built-in behavior: follow the main thinking level only when `/jarvis` follows the main model; pinned `/jarvis` models use `off`.
|
|
174
|
+
- `follow-main` follows the main thinking level even when `/jarvis` is pinned to a separate model.
|
|
175
|
+
- Explicit levels pin `/jarvis` thinking to that level.
|
|
176
|
+
- xAI `/jarvis` models still force thinking `off`.
|
|
177
|
+
|
|
178
|
+
### `/jarvis-thinking [--project|--global] clear`
|
|
179
|
+
Removes the selected scope so `/jarvis` thinking falls back through the remaining config layers to the built-in `auto` default.
|
|
180
|
+
|
|
181
|
+
### Side-session commands inside `/jarvis`
|
|
182
|
+
The `/jarvis` input handles a small set of built-in commands against the isolated side-session:
|
|
183
|
+
|
|
184
|
+
- `/compact [instructions]` compacts the `/jarvis` conversation context.
|
|
185
|
+
- `/tree` prints the `/jarvis` session tree with entry IDs.
|
|
186
|
+
- `/tree <entry-id>` navigates the `/jarvis` session tree to that entry.
|
|
187
|
+
- `/tree --summarize <entry-id> [instructions]` navigates and summarizes the branch being left.
|
|
188
|
+
- `/new` starts a fresh `/jarvis` side-session without changing the main Pi session.
|
|
189
|
+
|
|
168
190
|
---
|
|
169
191
|
|
|
170
|
-
## Model resolution
|
|
192
|
+
## Model and thinking resolution
|
|
171
193
|
|
|
172
194
|
```mermaid
|
|
173
195
|
flowchart TD
|
|
174
196
|
A[Project config\n.pi/jarvis.json] -->|if present| B{valid?}
|
|
175
|
-
B -->|yes| P[Use project
|
|
197
|
+
B -->|yes| P[Use project overrides]
|
|
176
198
|
B -->|no or missing| C[Global config\n<agentDir>/extensions/pi-jarvis.json]
|
|
177
199
|
C -->|if present| D{valid?}
|
|
178
|
-
D -->|yes| G[Use global
|
|
179
|
-
D -->|no or missing| E[Built-in
|
|
180
|
-
E --> F[follow-main]
|
|
200
|
+
D -->|yes| G[Use global overrides]
|
|
201
|
+
D -->|no or missing| E[Built-in defaults]
|
|
202
|
+
E --> F[Model default: follow-main]
|
|
203
|
+
E --> H[Thinking default: auto]
|
|
181
204
|
```
|
|
182
205
|
|
|
183
206
|
### Resolution order
|
|
184
207
|
|
|
208
|
+
Model and thinking settings resolve through the same config layers, then fall back to separate built-in defaults:
|
|
209
|
+
|
|
185
210
|
1. project config: `.pi/jarvis.json`
|
|
186
211
|
2. global config: `~/.pi/agent/extensions/pi-jarvis.json` or the equivalent path under a custom Pi agent dir
|
|
187
|
-
3. built-in
|
|
212
|
+
3. built-in defaults: model `follow-main`, thinking `auto`
|
|
188
213
|
|
|
189
214
|
---
|
|
190
215
|
|
|
@@ -246,7 +271,9 @@ sequenceDiagram
|
|
|
246
271
|
- `/jarvis` keeps its own isolated conversation state
|
|
247
272
|
- prior side-session history is restored from a session file under `jarvis-sessions/`
|
|
248
273
|
- Jarvis sees current main-session state plus a compact delta since the last `/jarvis` turn
|
|
249
|
-
-
|
|
274
|
+
- `/compact`, `/tree`, and `/new` entered inside `/jarvis` operate on the side-session, not the main session
|
|
275
|
+
- plain `/jarvis-model <provider/model>` writes the project model override; use `--global` to change the global default
|
|
276
|
+
- plain `/jarvis-thinking <level>` writes the project thinking override; use `--global` to change the global default
|
|
250
277
|
- thinking-step streaming is intentionally collapsed to a cleaner animated fallback for readability
|
|
251
278
|
|
|
252
279
|
---
|
|
@@ -320,10 +347,10 @@ Build the published package contents:
|
|
|
320
347
|
npm run build
|
|
321
348
|
```
|
|
322
349
|
|
|
323
|
-
Preview the npm payload:
|
|
350
|
+
Preview and verify the npm payload contract:
|
|
324
351
|
|
|
325
352
|
```bash
|
|
326
|
-
npm
|
|
353
|
+
npm run verify:release
|
|
327
354
|
```
|
|
328
355
|
|
|
329
356
|
---
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../index.ts"],"names":[],"mappings":"AAEA,OAAO,EAGN,KAAK,YAAY,EAGjB,MAAM,+BAA+B,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../index.ts"],"names":[],"mappings":"AAEA,OAAO,EAGN,KAAK,YAAY,EAGjB,MAAM,+BAA+B,CAAC;AA8FvC,MAAM,CAAC,OAAO,UAAU,eAAe,CAAC,EAAE,EAAE,YAAY,GAAG,IAAI,CA8c9D"}
|
package/dist/index.js
CHANGED
|
@@ -4,7 +4,7 @@ import { buildMainSessionContext } from "./main-context.js";
|
|
|
4
4
|
import { MainSessionTracker } from "./main-session-state.js";
|
|
5
5
|
import { attachOverlayBridge, JarvisOverlayBridge, JarvisOverlayComponent } from "./overlay.js";
|
|
6
6
|
import { createJarvisSessionRef, readJarvisSessionRef, JARVIS_SESSION_REF_CUSTOM_TYPE } from "./session-ref.js";
|
|
7
|
-
import { clearJarvisModelSelectionSetting, loadJarvisModelSelectionSetting, saveJarvisModelSelectionSetting } from "./jarvis-config.js";
|
|
7
|
+
import { clearJarvisModelSelectionSetting, clearJarvisThinkingSelectionSetting, loadJarvisModelSelectionSetting, loadJarvisThinkingSelectionSetting, saveJarvisModelSelectionSetting, saveJarvisThinkingSelectionSetting, } from "./jarvis-config.js";
|
|
8
8
|
import { JarvisSideSessionRuntime, createSideSessionFile } from "./side-session.js";
|
|
9
9
|
class StaleJarvisBootError extends Error {
|
|
10
10
|
constructor() {
|
|
@@ -25,6 +25,8 @@ export default function jarvisExtension(pi) {
|
|
|
25
25
|
queuedMessages: [],
|
|
26
26
|
jarvisModelSelection: { mode: "follow-main" },
|
|
27
27
|
jarvisModelSelectionSource: "default",
|
|
28
|
+
jarvisThinkingSelection: { mode: "auto" },
|
|
29
|
+
jarvisThinkingSelectionSource: "default",
|
|
28
30
|
systemPrompt: "",
|
|
29
31
|
themeProvider: () => {
|
|
30
32
|
throw new Error("/jarvis theme requested before UI was available.");
|
|
@@ -53,6 +55,7 @@ export default function jarvisExtension(pi) {
|
|
|
53
55
|
await ctx.ui.custom((tui, theme, _keybindings, done) => {
|
|
54
56
|
state.themeProvider = () => theme;
|
|
55
57
|
const closeOverlay = () => {
|
|
58
|
+
resetTransientAccessControls(state);
|
|
56
59
|
done(undefined);
|
|
57
60
|
queueMicrotask(() => tui.requestRender());
|
|
58
61
|
};
|
|
@@ -106,8 +109,8 @@ export default function jarvisExtension(pi) {
|
|
|
106
109
|
const clearScopedSelection = async () => {
|
|
107
110
|
const previousSelection = state.jarvisModelSelection;
|
|
108
111
|
const previousSource = state.jarvisModelSelectionSource;
|
|
109
|
-
const projectSelection = scope === "project" ? undefined :
|
|
110
|
-
const globalSelection = scope === "global" ? undefined :
|
|
112
|
+
const projectSelection = scope === "project" ? undefined : loadJarvisModelSelectionForClear(ctx.cwd, "project", ctx);
|
|
113
|
+
const globalSelection = scope === "global" ? undefined : loadJarvisModelSelectionForClear(ctx.cwd, "global", ctx);
|
|
111
114
|
const resolvedSelection = resolveJarvisModelSelectionFromSettings(projectSelection, globalSelection, ctx.modelRegistry);
|
|
112
115
|
await applyJarvisModelSelection(state, resolvedSelection.selection);
|
|
113
116
|
try {
|
|
@@ -188,6 +191,90 @@ export default function jarvisExtension(pi) {
|
|
|
188
191
|
await pinSelectedModel(selectedModel);
|
|
189
192
|
},
|
|
190
193
|
});
|
|
194
|
+
pi.registerCommand("jarvis-thinking", {
|
|
195
|
+
description: "Set the thinking level used by /jarvis without changing the main agent thinking level",
|
|
196
|
+
handler: async (args, ctx) => {
|
|
197
|
+
updateContextState(pi, state, ctx);
|
|
198
|
+
let parsedCommand;
|
|
199
|
+
try {
|
|
200
|
+
parsedCommand = parseJarvisThinkingCommand(args);
|
|
201
|
+
}
|
|
202
|
+
catch (error) {
|
|
203
|
+
ctx.ui.notify(error instanceof Error ? error.message : String(error), "error");
|
|
204
|
+
return;
|
|
205
|
+
}
|
|
206
|
+
const { request, scope, clearScope } = parsedCommand;
|
|
207
|
+
const scopeLabel = formatJarvisModelSelectionScope(scope);
|
|
208
|
+
const rollbackSelection = async (previousSelection, previousSource) => {
|
|
209
|
+
await applyJarvisThinkingSelection(state, previousSelection);
|
|
210
|
+
state.jarvisThinkingSelectionSource = previousSource;
|
|
211
|
+
};
|
|
212
|
+
const persistSelection = async (selection) => {
|
|
213
|
+
const previousSelection = state.jarvisThinkingSelection;
|
|
214
|
+
const previousSource = state.jarvisThinkingSelectionSource;
|
|
215
|
+
await applyJarvisThinkingSelection(state, selection);
|
|
216
|
+
try {
|
|
217
|
+
saveJarvisThinkingSelectionSetting(ctx.cwd, scope, selection);
|
|
218
|
+
state.jarvisThinkingSelectionSource = scope;
|
|
219
|
+
}
|
|
220
|
+
catch (error) {
|
|
221
|
+
await rollbackSelection(previousSelection, previousSource);
|
|
222
|
+
throw error;
|
|
223
|
+
}
|
|
224
|
+
};
|
|
225
|
+
const clearScopedSelection = async () => {
|
|
226
|
+
const previousSelection = state.jarvisThinkingSelection;
|
|
227
|
+
const previousSource = state.jarvisThinkingSelectionSource;
|
|
228
|
+
const projectSelection = scope === "project" ? undefined : loadJarvisThinkingSelectionForClear(ctx.cwd, "project", ctx);
|
|
229
|
+
const globalSelection = scope === "global" ? undefined : loadJarvisThinkingSelectionForClear(ctx.cwd, "global", ctx);
|
|
230
|
+
const resolvedSelection = resolveJarvisThinkingSelectionFromSettings(projectSelection, globalSelection);
|
|
231
|
+
await applyJarvisThinkingSelection(state, resolvedSelection.selection);
|
|
232
|
+
try {
|
|
233
|
+
clearJarvisThinkingSelectionSetting(ctx.cwd, scope);
|
|
234
|
+
state.jarvisThinkingSelectionSource = resolvedSelection.source;
|
|
235
|
+
return resolvedSelection;
|
|
236
|
+
}
|
|
237
|
+
catch (error) {
|
|
238
|
+
await rollbackSelection(previousSelection, previousSource);
|
|
239
|
+
throw error;
|
|
240
|
+
}
|
|
241
|
+
};
|
|
242
|
+
if (clearScope) {
|
|
243
|
+
try {
|
|
244
|
+
await clearScopedSelection();
|
|
245
|
+
}
|
|
246
|
+
catch (error) {
|
|
247
|
+
ctx.ui.notify(`Failed to clear the /jarvis thinking setting ${scopeLabel}: ${error instanceof Error ? error.message : String(error)}`, "error");
|
|
248
|
+
return;
|
|
249
|
+
}
|
|
250
|
+
ctx.ui.notify(`Cleared the /jarvis thinking setting ${scopeLabel}. /jarvis is now ${describeJarvisThinkingSelection(state)}.`, "info");
|
|
251
|
+
return;
|
|
252
|
+
}
|
|
253
|
+
const normalizedRequest = request.toLowerCase();
|
|
254
|
+
let selection;
|
|
255
|
+
if (normalizedRequest === "auto") {
|
|
256
|
+
selection = { mode: "auto" };
|
|
257
|
+
}
|
|
258
|
+
else if (normalizedRequest === "follow-main") {
|
|
259
|
+
selection = { mode: "follow-main" };
|
|
260
|
+
}
|
|
261
|
+
else if (isJarvisThinkingLevel(normalizedRequest)) {
|
|
262
|
+
selection = { mode: "pinned", thinkingLevel: normalizedRequest };
|
|
263
|
+
}
|
|
264
|
+
if (!selection) {
|
|
265
|
+
ctx.ui.notify(`/jarvis is ${describeJarvisThinkingSelection(state)}. Use /jarvis-thinking [--project|--global] clear, auto, follow-main, off, minimal, low, medium, high, or xhigh.`, request ? "error" : "info");
|
|
266
|
+
return;
|
|
267
|
+
}
|
|
268
|
+
try {
|
|
269
|
+
await persistSelection(selection);
|
|
270
|
+
}
|
|
271
|
+
catch (error) {
|
|
272
|
+
ctx.ui.notify(`Failed to set /jarvis thinking ${scopeLabel}: ${error instanceof Error ? error.message : String(error)}`, "error");
|
|
273
|
+
return;
|
|
274
|
+
}
|
|
275
|
+
ctx.ui.notify(`Set /jarvis thinking to ${formatJarvisThinkingSelection(selection)} ${scopeLabel}. Effective /jarvis thinking is ${getDesiredJarvisThinkingLevel(state) ?? "off"}.`, "info");
|
|
276
|
+
},
|
|
277
|
+
});
|
|
191
278
|
pi.on("session_start", async (_event, ctx) => {
|
|
192
279
|
state.bootGeneration += 1;
|
|
193
280
|
state.runtime?.dispose();
|
|
@@ -206,11 +293,20 @@ export default function jarvisExtension(pi) {
|
|
|
206
293
|
selection: { mode: "follow-main" },
|
|
207
294
|
source: "default",
|
|
208
295
|
};
|
|
296
|
+
let resolvedThinkingSelection = {
|
|
297
|
+
selection: { mode: "auto" },
|
|
298
|
+
source: "default",
|
|
299
|
+
};
|
|
209
300
|
resolvedSelection = resolveConfiguredJarvisModelSelection(ctx.cwd, ctx.modelRegistry, (scope, error) => {
|
|
210
301
|
ctx.ui.notify(`Failed to load the ${scope} /jarvis model setting: ${error instanceof Error ? error.message : String(error)}`, "error");
|
|
211
302
|
});
|
|
303
|
+
resolvedThinkingSelection = resolveConfiguredJarvisThinkingSelection(ctx.cwd, (scope, error) => {
|
|
304
|
+
ctx.ui.notify(`Failed to load the ${scope} /jarvis thinking setting: ${error instanceof Error ? error.message : String(error)}`, "error");
|
|
305
|
+
});
|
|
212
306
|
state.jarvisModelSelection = resolvedSelection.selection;
|
|
213
307
|
state.jarvisModelSelectionSource = resolvedSelection.source;
|
|
308
|
+
state.jarvisThinkingSelection = resolvedThinkingSelection.selection;
|
|
309
|
+
state.jarvisThinkingSelectionSource = resolvedThinkingSelection.source;
|
|
214
310
|
updateContextState(pi, state, ctx);
|
|
215
311
|
enforceCompatibilityGuard(state);
|
|
216
312
|
if (resolvedSelection.unavailable) {
|
|
@@ -259,7 +355,8 @@ export default function jarvisExtension(pi) {
|
|
|
259
355
|
state.mainSession.handleModelSelect(formatModelLabel(event.model));
|
|
260
356
|
refreshMainContext(state);
|
|
261
357
|
enforceCompatibilityGuard(state);
|
|
262
|
-
if (!state.runtime ||
|
|
358
|
+
if (!state.runtime ||
|
|
359
|
+
(state.jarvisModelSelection.mode !== "follow-main" && state.jarvisThinkingSelection.mode !== "follow-main")) {
|
|
263
360
|
return;
|
|
264
361
|
}
|
|
265
362
|
try {
|
|
@@ -279,6 +376,8 @@ export default function jarvisExtension(pi) {
|
|
|
279
376
|
state.lastJarvisSeenMainContext = undefined;
|
|
280
377
|
state.jarvisModelSelection = { mode: "follow-main" };
|
|
281
378
|
state.jarvisModelSelectionSource = "default";
|
|
379
|
+
state.jarvisThinkingSelection = { mode: "auto" };
|
|
380
|
+
state.jarvisThinkingSelectionSource = "default";
|
|
282
381
|
state.allowSideTools = false;
|
|
283
382
|
state.allowFollowUpToMain = false;
|
|
284
383
|
state.allowSteerToMain = false;
|
|
@@ -301,6 +400,12 @@ function updateContextState(pi, state, ctx) {
|
|
|
301
400
|
function refreshMainContext(state) {
|
|
302
401
|
state.mainContext = buildMainSessionContext(state.mainSession.snapshot());
|
|
303
402
|
}
|
|
403
|
+
function resetTransientAccessControls(state) {
|
|
404
|
+
state.allowSideTools = false;
|
|
405
|
+
state.allowFollowUpToMain = false;
|
|
406
|
+
state.allowSteerToMain = false;
|
|
407
|
+
state.runtime?.setToolAccessEnabled(false);
|
|
408
|
+
}
|
|
304
409
|
function getDesiredJarvisModel(state) {
|
|
305
410
|
return state.jarvisModelSelection.mode === "pinned" ? state.jarvisModelSelection.model : state.model;
|
|
306
411
|
}
|
|
@@ -309,6 +414,12 @@ function getDesiredJarvisThinkingLevel(state) {
|
|
|
309
414
|
if (desiredModel?.provider === "xai") {
|
|
310
415
|
return "off";
|
|
311
416
|
}
|
|
417
|
+
if (state.jarvisThinkingSelection.mode === "pinned") {
|
|
418
|
+
return state.jarvisThinkingSelection.thinkingLevel;
|
|
419
|
+
}
|
|
420
|
+
if (state.jarvisThinkingSelection.mode === "follow-main") {
|
|
421
|
+
return state.thinkingLevel;
|
|
422
|
+
}
|
|
312
423
|
return state.jarvisModelSelection.mode === "follow-main" ? state.thinkingLevel : "off";
|
|
313
424
|
}
|
|
314
425
|
function toStoredJarvisModelSelection(selection) {
|
|
@@ -351,6 +462,30 @@ function parseJarvisModelCommand(args) {
|
|
|
351
462
|
clearScope: request.toLowerCase() === "clear",
|
|
352
463
|
};
|
|
353
464
|
}
|
|
465
|
+
function parseJarvisThinkingCommand(args) {
|
|
466
|
+
const parsed = parseJarvisModelCommand(args);
|
|
467
|
+
return {
|
|
468
|
+
request: parsed.request,
|
|
469
|
+
scope: parsed.scope,
|
|
470
|
+
clearScope: parsed.clearScope,
|
|
471
|
+
};
|
|
472
|
+
}
|
|
473
|
+
function isJarvisThinkingLevel(value) {
|
|
474
|
+
return value === "off" || value === "minimal" || value === "low" || value === "medium" || value === "high" || value === "xhigh";
|
|
475
|
+
}
|
|
476
|
+
function formatJarvisThinkingSelection(selection) {
|
|
477
|
+
if (selection.mode === "auto") {
|
|
478
|
+
return "auto";
|
|
479
|
+
}
|
|
480
|
+
if (selection.mode === "follow-main") {
|
|
481
|
+
return "follow-main";
|
|
482
|
+
}
|
|
483
|
+
return selection.thinkingLevel;
|
|
484
|
+
}
|
|
485
|
+
function describeJarvisThinkingSelection(state) {
|
|
486
|
+
const sourceLabel = formatJarvisModelSelectionSource(state.jarvisThinkingSelectionSource);
|
|
487
|
+
return `thinking ${formatJarvisThinkingSelection(state.jarvisThinkingSelection)} (effective ${getDesiredJarvisThinkingLevel(state) ?? "off"}; ${sourceLabel})`;
|
|
488
|
+
}
|
|
354
489
|
function resolveStoredJarvisModelSelection(storedSelection, source, modelRegistry) {
|
|
355
490
|
if (storedSelection.mode === "follow-main") {
|
|
356
491
|
return {
|
|
@@ -416,6 +551,50 @@ function resolveConfiguredJarvisModelSelection(cwd, modelRegistry, onScopeError)
|
|
|
416
551
|
}
|
|
417
552
|
return resolveJarvisModelSelectionFromSettings(projectSelection, globalSelection, modelRegistry);
|
|
418
553
|
}
|
|
554
|
+
function resolveJarvisThinkingSelectionFromSettings(projectSelection, globalSelection) {
|
|
555
|
+
if (projectSelection) {
|
|
556
|
+
return { selection: projectSelection, source: "project" };
|
|
557
|
+
}
|
|
558
|
+
if (globalSelection) {
|
|
559
|
+
return { selection: globalSelection, source: "global" };
|
|
560
|
+
}
|
|
561
|
+
return { selection: { mode: "auto" }, source: "default" };
|
|
562
|
+
}
|
|
563
|
+
function resolveConfiguredJarvisThinkingSelection(cwd, onScopeError) {
|
|
564
|
+
let projectSelection;
|
|
565
|
+
let globalSelection;
|
|
566
|
+
try {
|
|
567
|
+
projectSelection = loadJarvisThinkingSelectionSetting(cwd, "project");
|
|
568
|
+
}
|
|
569
|
+
catch (error) {
|
|
570
|
+
onScopeError?.("project", error);
|
|
571
|
+
}
|
|
572
|
+
try {
|
|
573
|
+
globalSelection = loadJarvisThinkingSelectionSetting(cwd, "global");
|
|
574
|
+
}
|
|
575
|
+
catch (error) {
|
|
576
|
+
onScopeError?.("global", error);
|
|
577
|
+
}
|
|
578
|
+
return resolveJarvisThinkingSelectionFromSettings(projectSelection, globalSelection);
|
|
579
|
+
}
|
|
580
|
+
function loadJarvisModelSelectionForClear(cwd, scope, ctx) {
|
|
581
|
+
try {
|
|
582
|
+
return loadJarvisModelSelectionSetting(cwd, scope);
|
|
583
|
+
}
|
|
584
|
+
catch (error) {
|
|
585
|
+
ctx.ui.notify(`Ignoring malformed ${scope} /jarvis model setting while clearing the requested scope: ${error instanceof Error ? error.message : String(error)}`, "warning");
|
|
586
|
+
return undefined;
|
|
587
|
+
}
|
|
588
|
+
}
|
|
589
|
+
function loadJarvisThinkingSelectionForClear(cwd, scope, ctx) {
|
|
590
|
+
try {
|
|
591
|
+
return loadJarvisThinkingSelectionSetting(cwd, scope);
|
|
592
|
+
}
|
|
593
|
+
catch (error) {
|
|
594
|
+
ctx.ui.notify(`Ignoring malformed ${scope} /jarvis thinking setting while clearing the requested scope: ${error instanceof Error ? error.message : String(error)}`, "warning");
|
|
595
|
+
return undefined;
|
|
596
|
+
}
|
|
597
|
+
}
|
|
419
598
|
function isModelBridgeCompatible(model) {
|
|
420
599
|
if (!model) {
|
|
421
600
|
return true;
|
|
@@ -509,6 +688,54 @@ async function applyJarvisModelSelection(state, selection) {
|
|
|
509
688
|
throw error;
|
|
510
689
|
}
|
|
511
690
|
}
|
|
691
|
+
async function applyJarvisThinkingSelection(state, selection) {
|
|
692
|
+
const previousSelection = state.jarvisThinkingSelection;
|
|
693
|
+
state.jarvisThinkingSelection = selection;
|
|
694
|
+
try {
|
|
695
|
+
await syncRuntimeModelSelection(state);
|
|
696
|
+
}
|
|
697
|
+
catch (error) {
|
|
698
|
+
state.jarvisThinkingSelection = previousSelection;
|
|
699
|
+
try {
|
|
700
|
+
await syncRuntimeModelSelection(state);
|
|
701
|
+
}
|
|
702
|
+
catch {
|
|
703
|
+
// Keep the original sync failure; rollback is best-effort only.
|
|
704
|
+
}
|
|
705
|
+
throw error;
|
|
706
|
+
}
|
|
707
|
+
}
|
|
708
|
+
async function executeJarvisSideCommand(pi, state, ctx, runtime, command) {
|
|
709
|
+
if (command.name === "compact") {
|
|
710
|
+
await runtime.compactJarvisContext(command.customInstructions);
|
|
711
|
+
return runtime;
|
|
712
|
+
}
|
|
713
|
+
if (command.name === "tree") {
|
|
714
|
+
if (!command.targetId) {
|
|
715
|
+
runtime.addSystemMessage(runtime.describeSessionTree());
|
|
716
|
+
return runtime;
|
|
717
|
+
}
|
|
718
|
+
await runtime.navigateSessionTree(command.targetId, {
|
|
719
|
+
summarize: command.summarize,
|
|
720
|
+
customInstructions: command.customInstructions,
|
|
721
|
+
});
|
|
722
|
+
return runtime;
|
|
723
|
+
}
|
|
724
|
+
state.bootGeneration += 1;
|
|
725
|
+
state.runtime?.dispose();
|
|
726
|
+
state.runtime = undefined;
|
|
727
|
+
state.bootPromise = undefined;
|
|
728
|
+
state.flushPromise = undefined;
|
|
729
|
+
state.lastJarvisSeenMainContext = undefined;
|
|
730
|
+
resetTransientAccessControls(state);
|
|
731
|
+
const sessionFile = await createSideSessionFile(ctx.cwd);
|
|
732
|
+
const sessionRef = createJarvisSessionRef(sessionFile);
|
|
733
|
+
state.sessionRef = sessionRef;
|
|
734
|
+
pi.appendEntry(JARVIS_SESSION_REF_CUSTOM_TYPE, sessionRef);
|
|
735
|
+
const nextRuntime = await ensureRuntime(pi, state, ctx);
|
|
736
|
+
nextRuntime.addSystemMessage("Started a new /jarvis side session.");
|
|
737
|
+
return nextRuntime;
|
|
738
|
+
}
|
|
512
739
|
function normalizeInitialMessage(args) {
|
|
513
740
|
const message = args.trim();
|
|
514
741
|
return message.length > 0 ? message : undefined;
|
|
@@ -516,6 +743,38 @@ function normalizeInitialMessage(args) {
|
|
|
516
743
|
function queueMessage(state, message) {
|
|
517
744
|
state.queuedMessages.push(message);
|
|
518
745
|
}
|
|
746
|
+
function parseJarvisSideCommand(message) {
|
|
747
|
+
const text = message.trim();
|
|
748
|
+
if (text === "/new") {
|
|
749
|
+
return { name: "new" };
|
|
750
|
+
}
|
|
751
|
+
if (text === "/compact") {
|
|
752
|
+
return { name: "compact" };
|
|
753
|
+
}
|
|
754
|
+
if (text.startsWith("/compact ")) {
|
|
755
|
+
return { name: "compact", customInstructions: text.slice("/compact ".length).trim() || undefined };
|
|
756
|
+
}
|
|
757
|
+
if (text === "/tree") {
|
|
758
|
+
return { name: "tree", summarize: false };
|
|
759
|
+
}
|
|
760
|
+
if (text.startsWith("/tree ")) {
|
|
761
|
+
const args = text.slice("/tree ".length).trim();
|
|
762
|
+
const summarizePrefix = "--summarize ";
|
|
763
|
+
if (args.startsWith(summarizePrefix)) {
|
|
764
|
+
const rest = args.slice(summarizePrefix.length).trim();
|
|
765
|
+
const [targetId, ...instructionTokens] = rest.split(/\s+/).filter(Boolean);
|
|
766
|
+
return {
|
|
767
|
+
name: "tree",
|
|
768
|
+
targetId,
|
|
769
|
+
summarize: true,
|
|
770
|
+
customInstructions: instructionTokens.join(" ").trim() || undefined,
|
|
771
|
+
};
|
|
772
|
+
}
|
|
773
|
+
const [targetId] = args.split(/\s+/).filter(Boolean);
|
|
774
|
+
return { name: "tree", targetId, summarize: false };
|
|
775
|
+
}
|
|
776
|
+
return undefined;
|
|
777
|
+
}
|
|
519
778
|
function createOverlayView(pi, state, ctx) {
|
|
520
779
|
const syncToolAccess = () => {
|
|
521
780
|
state.runtime?.setToolAccessEnabled(state.allowSideTools);
|
|
@@ -610,6 +869,9 @@ function formatMainContextDeltaLabel(previousContext, currentContext) {
|
|
|
610
869
|
if (currentContext.summary.latestUserRequest && currentContext.summary.latestUserRequest !== previousContext.summary.latestUserRequest) {
|
|
611
870
|
return `request → ${currentContext.summary.latestUserRequest}`;
|
|
612
871
|
}
|
|
872
|
+
if (currentContext.summary.latestAssistantText && currentContext.summary.latestAssistantText !== previousContext.summary.latestAssistantText) {
|
|
873
|
+
return `assistant → ${currentContext.summary.latestAssistantText}`;
|
|
874
|
+
}
|
|
613
875
|
return "no significant change";
|
|
614
876
|
}
|
|
615
877
|
async function flushQueuedMessages(pi, state, ctx) {
|
|
@@ -624,17 +886,24 @@ async function flushQueuedMessages(pi, state, ctx) {
|
|
|
624
886
|
// rethrowing.
|
|
625
887
|
state.flushPromise = (async () => {
|
|
626
888
|
try {
|
|
627
|
-
|
|
889
|
+
let runtime = await ensureRuntime(pi, state, ctx);
|
|
628
890
|
await runtime.syncModel(getDesiredJarvisModel(state), getDesiredJarvisThinkingLevel(state));
|
|
629
891
|
while (state.queuedMessages.length > 0) {
|
|
630
892
|
const message = state.queuedMessages[0];
|
|
893
|
+
const command = parseJarvisSideCommand(message);
|
|
631
894
|
try {
|
|
632
|
-
|
|
895
|
+
if (command) {
|
|
896
|
+
runtime = await executeJarvisSideCommand(pi, state, ctx, runtime, command);
|
|
897
|
+
}
|
|
898
|
+
else {
|
|
899
|
+
await runtime.sendMessage(message);
|
|
900
|
+
state.lastJarvisSeenMainContext = state.mainContext;
|
|
901
|
+
}
|
|
633
902
|
state.queuedMessages.shift();
|
|
634
|
-
state.lastJarvisSeenMainContext = state.mainContext;
|
|
635
903
|
}
|
|
636
904
|
catch (error) {
|
|
637
|
-
|
|
905
|
+
const action = command ? `run ${message.split(/\s+/, 1)[0]}` : "send /jarvis message";
|
|
906
|
+
state.bridge.notify(`Failed to ${action}: ${error instanceof Error ? error.message : String(error)}`, "error");
|
|
638
907
|
break;
|
|
639
908
|
}
|
|
640
909
|
}
|