pi-lean-dimension 0.2.3 → 0.3.0
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 +1 -1
- package/node_modules/pi-lean-portal/AGENTS.md +15 -23
- package/node_modules/pi-lean-portal/README.md +31 -1
- package/node_modules/pi-lean-portal/__tests__/browser-toggle-profile.test.ts +12 -1
- package/node_modules/pi-lean-portal/__tests__/browser-toggle.test.ts +263 -1102
- package/node_modules/pi-lean-portal/__tests__/fetch-backend.test.ts +6 -32
- package/node_modules/pi-lean-portal/__tests__/helpers/mock-python-bridge.py +1 -1
- package/node_modules/pi-lean-portal/__tests__/helpers/probe-user-backend.ts +1 -2
- package/node_modules/pi-lean-portal/__tests__/helpers/setup-temp-dir.ts +26 -0
- package/node_modules/pi-lean-portal/__tests__/plugin-config-browser.test.ts +1 -1
- package/node_modules/pi-lean-portal/__tests__/plugin-contract.test.ts +1 -1
- package/node_modules/pi-lean-portal/__tests__/plugin-loading.test.ts +1 -1
- package/node_modules/pi-lean-portal/__tests__/probe-user-backend.test.ts +1 -1
- package/node_modules/pi-lean-portal/__tests__/python-adapter.test.ts +65 -69
- package/node_modules/pi-lean-portal/__tests__/router-dispatch.test.ts +1 -14
- package/node_modules/pi-lean-portal/__tests__/run-contributed-suites.test.ts +31 -16
- package/node_modules/pi-lean-portal/__tests__/snapshot-cache.test.ts +1 -1
- package/node_modules/pi-lean-portal/__tests__/web-guides.test.ts +146 -19
- package/node_modules/pi-lean-portal/backends/chromium-py/__pycache__/bridge.cpython-313.pyc +0 -0
- package/node_modules/pi-lean-portal/backends/firefox-py/__pycache__/bridge.cpython-313.pyc +0 -0
- package/node_modules/pi-lean-portal/backends/playwright-base/playwright-plugin.ts +1 -1
- package/node_modules/pi-lean-portal/backends/python-adapter.ts +3 -3
- package/node_modules/pi-lean-portal/backends/python-base/pi_browser_bridge/__init__.py +2 -4
- package/node_modules/pi-lean-portal/backends/python-base/pi_browser_bridge/__pycache__/__init__.cpython-312.pyc +0 -0
- package/node_modules/pi-lean-portal/backends/python-base/pi_browser_bridge/__pycache__/__init__.cpython-313.pyc +0 -0
- package/node_modules/pi-lean-portal/backends/python-base/pi_browser_bridge/__pycache__/bridge.cpython-312.pyc +0 -0
- package/node_modules/pi-lean-portal/backends/python-base/pi_browser_bridge/__pycache__/playwright_base.cpython-312.pyc +0 -0
- package/node_modules/pi-lean-portal/backends/python-base/pi_browser_bridge/__pycache__/playwright_base.cpython-313.pyc +0 -0
- package/node_modules/pi-lean-portal/backends/python-base/pi_browser_bridge/playwright_base.py +572 -81
- package/node_modules/pi-lean-portal/backends/python-base/tests/__pycache__/conftest.cpython-313-pytest-9.1.1.pyc +0 -0
- package/node_modules/pi-lean-portal/backends/python-base/tests/__pycache__/test_accessibility.cpython-313-pytest-9.1.1.pyc +0 -0
- package/node_modules/pi-lean-portal/backends/python-base/tests/__pycache__/test_bot_detection.cpython-313-pytest-9.1.1.pyc +0 -0
- package/node_modules/pi-lean-portal/backends/python-base/tests/__pycache__/test_browser_data.cpython-313-pytest-9.1.1.pyc +0 -0
- package/node_modules/pi-lean-portal/backends/python-base/tests/__pycache__/test_playwright_base_quirks.cpython-313-pytest-9.1.1.pyc +0 -0
- package/node_modules/pi-lean-portal/backends/python-base/tests/__pycache__/test_py_bridges.cpython-313-pytest-9.1.1.pyc +0 -0
- package/node_modules/pi-lean-portal/backends/python-base/tests/test_playwright_base_quirks.py +13 -43
- package/node_modules/pi-lean-portal/browser-profile.ts +2 -3
- package/node_modules/pi-lean-portal/browser-toggle.ts +168 -380
- package/node_modules/pi-lean-portal/contributed/CHOOSING.md +2 -2
- package/node_modules/pi-lean-portal/contributed/README.md +65 -6
- package/node_modules/pi-lean-portal/contributed/camoufox-py/__pycache__/bridge.cpython-312.pyc +0 -0
- package/node_modules/pi-lean-portal/contributed/camoufox-py/bridge.py +108 -14
- package/node_modules/pi-lean-portal/contributed/camoufox-py/pin.json +4 -0
- package/node_modules/pi-lean-portal/contributed/invisible-py/bridge.py +1 -1
- package/node_modules/pi-lean-portal/core/fetch-backend.ts +10 -30
- package/node_modules/pi-lean-portal/core/guides.ts +14 -18
- package/node_modules/pi-lean-portal/core/router.ts +2 -20
- package/node_modules/pi-lean-portal/core/shared/bot-detection.ts +19 -30
- package/node_modules/pi-lean-portal/core/shared/browser-data.ts +4 -4
- package/node_modules/pi-lean-portal/core/shared/nav-settle.ts +2 -2
- package/node_modules/pi-lean-portal/core/shared/paths.ts +16 -2
- package/node_modules/pi-lean-portal/core/shared/settings-reader.ts +2 -7
- package/node_modules/pi-lean-portal/core/shared/snapshot-cache.ts +1 -1
- package/node_modules/pi-lean-portal/index.ts +8 -1
- package/node_modules/pi-lean-portal/package.json +2 -1
- package/node_modules/pi-lean-portal/tools/web-learn.ts +1 -4
- package/node_modules/pi-lean-search/__tests__/web-search.test.ts +87 -0
- package/node_modules/pi-lean-search/index.ts +134 -64
- package/node_modules/pi-lean-search/package.json +2 -1
- package/node_modules/playwright/README.md +3 -3
- package/node_modules/playwright/lib/agents/copilot-setup-steps.yml +2 -2
- package/node_modules/playwright/lib/cli/reportActions.js +2 -2
- package/node_modules/playwright/lib/common/index.js +77 -52
- package/node_modules/playwright/lib/common/index.js.txt +10 -11
- package/node_modules/playwright/lib/errorContext.js +2 -3
- package/node_modules/playwright/lib/index.js +30 -8
- package/node_modules/playwright/lib/matchers/expect.js +4811 -4379
- package/node_modules/playwright/lib/matchers/expect.js.LICENSE +87 -140
- package/node_modules/playwright/lib/matchers/expect.js.txt +30 -35
- package/node_modules/playwright/lib/program.js +19 -0
- package/node_modules/playwright/lib/runner/index.js +309 -153
- package/node_modules/playwright/lib/runner/index.js.txt +13 -12
- package/node_modules/playwright/lib/transform/babelBundle.js +12899 -11654
- package/node_modules/playwright/lib/transform/babelBundle.js.LICENSE +379 -354
- package/node_modules/playwright/lib/transform/babelBundle.js.txt +39 -39
- package/node_modules/playwright/lib/transform/esmLoader.js +3129 -1116
- package/node_modules/playwright/lib/transform/esmLoader.js.LICENSE +81 -61
- package/node_modules/playwright/lib/transform/esmLoader.js.txt +21 -18
- package/node_modules/playwright/lib/util.js +2 -29
- package/node_modules/playwright/lib/worker/workerProcessEntry.js +14 -32
- package/node_modules/playwright/lib/worker/workerProcessEntry.js.txt +7 -8
- package/node_modules/playwright/package.json +3 -6
- package/node_modules/playwright/types/test.d.ts +391 -4
- package/node_modules/playwright/types/testReporter.d.ts +79 -4
- package/node_modules/playwright-core/browsers.json +12 -18
- package/node_modules/playwright-core/lib/bootstrap.js +1 -1
- package/node_modules/playwright-core/lib/coreBundle.js +11344 -9900
- package/node_modules/playwright-core/lib/server/electron/loader.js +5 -2
- package/node_modules/playwright-core/lib/serverRegistry.js +5 -1
- package/node_modules/playwright-core/lib/tools/cli-client/help.json +21 -6
- package/node_modules/playwright-core/lib/tools/cli-client/program.js +1 -1
- package/node_modules/playwright-core/lib/tools/cli-client/session.js +4 -0
- package/node_modules/playwright-core/lib/tools/{cli-client/skill → skills/playwright-cli}/SKILL.md +21 -5
- package/node_modules/playwright-core/lib/tools/{cli-client/skill/references/spec-driven-testing.md → skills/playwright-cli/references/test-generation.md} +135 -7
- package/node_modules/playwright-core/lib/tools/skills/playwright-component-testing/SKILL.md +143 -0
- package/node_modules/playwright-core/lib/tools/skills/playwright-component-testing/references/gallery-spec.md +144 -0
- package/node_modules/playwright-core/lib/tools/skills/playwright-component-testing/references/migration.md +85 -0
- package/node_modules/playwright-core/lib/tools/skills/playwright-component-testing/references/react.md +67 -0
- package/node_modules/playwright-core/lib/tools/skills/playwright-component-testing/references/vue.md +75 -0
- package/node_modules/playwright-core/lib/tools/utils/extension.js +31 -8
- package/node_modules/playwright-core/lib/utilsBundle.js +37698 -33670
- package/node_modules/playwright-core/lib/utilsBundle.js.LICENSE +391 -268
- package/node_modules/playwright-core/lib/vite/dashboard/assets/index-CyWAfh-p.js +11 -0
- package/node_modules/playwright-core/lib/vite/dashboard/assets/index-DhC616m4.css +1 -0
- package/node_modules/playwright-core/lib/vite/dashboard/index.html +2 -2
- package/node_modules/playwright-core/lib/vite/htmlReport/report.css +2 -1
- package/node_modules/playwright-core/lib/vite/htmlReport/report.js +15 -55
- package/node_modules/playwright-core/lib/vite/recorder/assets/codeMirrorModule--QdMvsKi.css +1 -0
- package/node_modules/playwright-core/lib/vite/recorder/assets/codeMirrorModule-CwYYHbcZ.js +32 -0
- package/node_modules/playwright-core/lib/vite/recorder/assets/index-DYjdXIbE.js +129 -0
- package/node_modules/playwright-core/lib/vite/recorder/assets/index-l_lX622x.css +1 -0
- package/node_modules/playwright-core/lib/vite/recorder/index.html +2 -2
- package/node_modules/playwright-core/lib/vite/traceViewer/assets/codeMirrorModule-By56iMx7.js +32 -0
- package/node_modules/playwright-core/lib/vite/traceViewer/assets/defaultSettingsView-B34OrIms.js +181 -0
- package/node_modules/playwright-core/lib/vite/traceViewer/assets/urlMatch-L3liM589.js +1 -0
- package/node_modules/playwright-core/lib/vite/traceViewer/assets/xtermModule-COQkjINf.js +7 -0
- package/node_modules/playwright-core/lib/vite/traceViewer/codeMirrorModule.-QdMvsKi.css +1 -0
- package/node_modules/playwright-core/lib/vite/traceViewer/defaultSettingsView.BLFoOugd.css +1 -0
- package/node_modules/playwright-core/lib/vite/traceViewer/index.B_TqY17P.css +1 -0
- package/node_modules/playwright-core/lib/vite/traceViewer/index.Dl36UVQT.js +1 -0
- package/node_modules/playwright-core/lib/vite/traceViewer/index.html +5 -5
- package/node_modules/playwright-core/lib/vite/traceViewer/snapshot.B_Jk1wbt.js +1 -0
- package/node_modules/playwright-core/lib/vite/traceViewer/snapshot.html +2 -2
- package/node_modules/playwright-core/lib/vite/traceViewer/sw.bundle.js +3 -4
- package/node_modules/playwright-core/lib/vite/traceViewer/uiMode.C7UW1sC9.css +1 -0
- package/node_modules/playwright-core/lib/vite/traceViewer/uiMode.D962mr9b.js +5 -0
- package/node_modules/playwright-core/lib/vite/traceViewer/uiMode.html +5 -5
- package/node_modules/playwright-core/lib/vite/traceViewer/xtermModule.kHJ-D0s7.css +1 -0
- package/node_modules/playwright-core/lib/webp_codec.LICENSE +173 -0
- package/node_modules/playwright-core/lib/webp_codec.wasm +0 -0
- package/node_modules/playwright-core/lib/xdg-open +338 -137
- package/node_modules/playwright-core/package.json +2 -2
- package/node_modules/playwright-core/types/protocol.d.ts +249 -36
- package/node_modules/playwright-core/types/structs.d.ts +3 -1
- package/node_modules/playwright-core/types/types.d.ts +2009 -266
- package/package.json +3 -3
- package/node_modules/pi-lean-portal/__tests__/url-safety.test.ts +0 -188
- package/node_modules/pi-lean-portal/backends/python-base/pi_browser_bridge/__pycache__/bridge.cpython-313.pyc +0 -0
- package/node_modules/pi-lean-portal/backends/python-base/pi_browser_bridge/__pycache__/patch_playwright.cpython-313.pyc +0 -0
- package/node_modules/pi-lean-portal/backends/python-base/pi_browser_bridge/bridge.py +0 -525
- package/node_modules/pi-lean-portal/backends/python-base/tests/__pycache__/test_accessibility.cpython-313.pyc +0 -0
- package/node_modules/pi-lean-portal/backends/python-base/tests/__pycache__/test_bot_detection.cpython-313.pyc +0 -0
- package/node_modules/pi-lean-portal/backends/python-base/tests/__pycache__/test_browser_data.cpython-313.pyc +0 -0
- package/node_modules/pi-lean-portal/backends/python-base/tests/__pycache__/test_chromium_py_bridge.cpython-313-pytest-9.1.1.pyc +0 -0
- package/node_modules/pi-lean-portal/backends/python-base/tests/__pycache__/test_firefox_py_bridge.cpython-313-pytest-9.1.1.pyc +0 -0
- package/node_modules/pi-lean-portal/backends/python-base/tests/__pycache__/test_playwright_base_quirks.cpython-313.pyc +0 -0
- package/node_modules/pi-lean-portal/core/shared/url-safety.ts +0 -164
- package/node_modules/playwright-core/lib/server/deviceDescriptorsSource.json +0 -2739
- package/node_modules/playwright-core/lib/tools/cli-client/skill/references/test-generation.md +0 -134
- package/node_modules/playwright-core/lib/vite/dashboard/assets/index-BY2S1tHT.css +0 -1
- package/node_modules/playwright-core/lib/vite/dashboard/assets/index-C_5TMfeg.js +0 -52
- package/node_modules/playwright-core/lib/vite/recorder/assets/codeMirrorModule-DYBRYzYX.css +0 -1
- package/node_modules/playwright-core/lib/vite/recorder/assets/codeMirrorModule-DeBYQozu.js +0 -32
- package/node_modules/playwright-core/lib/vite/recorder/assets/index-4ZiSSCmn.css +0 -1
- package/node_modules/playwright-core/lib/vite/recorder/assets/index-Bq-mQf8S.js +0 -193
- package/node_modules/playwright-core/lib/vite/traceViewer/assets/codeMirrorModule-LEHpjmcn.js +0 -32
- package/node_modules/playwright-core/lib/vite/traceViewer/assets/defaultSettingsView-BNmKHKpQ.js +0 -264
- package/node_modules/playwright-core/lib/vite/traceViewer/assets/urlMatch-BYQrIQwR.js +0 -1
- package/node_modules/playwright-core/lib/vite/traceViewer/assets/xtermModule-CsJ4vdCR.js +0 -9
- package/node_modules/playwright-core/lib/vite/traceViewer/codeMirrorModule.DYBRYzYX.css +0 -1
- package/node_modules/playwright-core/lib/vite/traceViewer/defaultSettingsView.CjdS-WJx.css +0 -1
- package/node_modules/playwright-core/lib/vite/traceViewer/index.CzXZzn5A.css +0 -1
- package/node_modules/playwright-core/lib/vite/traceViewer/index.DMMX1gXU.js +0 -2
- package/node_modules/playwright-core/lib/vite/traceViewer/snapshot.v8KI4P3m.js +0 -2
- package/node_modules/playwright-core/lib/vite/traceViewer/uiMode.BZQ54Kgt.css +0 -1
- package/node_modules/playwright-core/lib/vite/traceViewer/uiMode.Ut8wwJNp.js +0 -6
- package/node_modules/playwright-core/lib/vite/traceViewer/xtermModule.DYP7pi_n.css +0 -32
- /package/node_modules/playwright-core/lib/tools/{cli-client/skill → skills/playwright-cli}/references/element-attributes.md +0 -0
- /package/node_modules/playwright-core/lib/tools/{cli-client/skill → skills/playwright-cli}/references/playwright-tests.md +0 -0
- /package/node_modules/playwright-core/lib/tools/{cli-client/skill → skills/playwright-cli}/references/request-mocking.md +0 -0
- /package/node_modules/playwright-core/lib/tools/{cli-client/skill → skills/playwright-cli}/references/running-code.md +0 -0
- /package/node_modules/playwright-core/lib/tools/{cli-client/skill → skills/playwright-cli}/references/session-management.md +0 -0
- /package/node_modules/playwright-core/lib/tools/{cli-client/skill → skills/playwright-cli}/references/storage-state.md +0 -0
- /package/node_modules/playwright-core/lib/tools/{cli-client/skill → skills/playwright-cli}/references/tracing.md +0 -0
- /package/node_modules/playwright-core/lib/tools/{cli-client/skill → skills/playwright-cli}/references/video-recording.md +0 -0
- /package/node_modules/playwright-core/lib/tools/{trace → skills/playwright-trace}/SKILL.md +0 -0
|
@@ -2,452 +2,251 @@ import type {
|
|
|
2
2
|
ExtensionAPI,
|
|
3
3
|
ExtensionCommandContext,
|
|
4
4
|
ExtensionContext,
|
|
5
|
+
ThemeColor,
|
|
5
6
|
} from "@earendil-works/pi-coding-agent";
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
* browser automation tools (browsing tools) and learn tools (web-learn) in the system prompt.
|
|
13
|
-
*
|
|
14
|
-
* Three states: on (browsing only), learn (browsing + web-learn), off (all disabled).
|
|
15
|
-
* Internally stores two independent booleans (browserToolsEnabled, learnToolsEnabled).
|
|
16
|
-
*
|
|
17
|
-
* When browser tools are disabled, the LLM cannot see them — no descriptions,
|
|
18
|
-
* parameter schemas, prompt snippets, or guidelines are included in the context,
|
|
19
|
-
* saving roughly 1500–2000 tokens per turn.
|
|
20
|
-
*
|
|
21
|
-
* ── Stateless fallback ─────────────────────────────────────────
|
|
22
|
-
*
|
|
23
|
-
* web-fetch is a stateless HTTP tool (no session, no interactivity). If you
|
|
24
|
-
* only need quick page content and never use the interactive browser tools,
|
|
25
|
-
* you can leave browser tools disabled and rely on web-fetch alone.
|
|
26
|
-
*/
|
|
27
|
-
|
|
7
|
+
import {
|
|
8
|
+
defineToolset,
|
|
9
|
+
TOOLSET_EVENTS,
|
|
10
|
+
getDefaultResolutionMode,
|
|
11
|
+
} from "pi-tool-masking";
|
|
12
|
+
import type { ToolsetSpec } from "pi-tool-masking";
|
|
28
13
|
import { readMergedSettings } from "./core/shared/settings-reader.js";
|
|
29
14
|
|
|
30
|
-
// ----
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
"web
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
/** Names of learn tools (web-learn) that require /web learn to be active. */
|
|
51
|
-
const LEARN_TOOL_NAMES = new Set(["web-learn"]);
|
|
52
|
-
|
|
53
|
-
/**
|
|
54
|
-
* Names of sibling-package tools that are toggled alongside browser tools
|
|
55
|
-
* by /web on|off|learn.
|
|
56
|
-
*
|
|
57
|
-
* - "web-search": SearXNG search from pi-lean-search (Sprint 4).
|
|
58
|
-
*
|
|
59
|
-
* Exact-name `Set.has()` membership — NO regex (avoids false positives
|
|
60
|
-
* on third-party web-* tools).
|
|
61
|
-
*/
|
|
62
|
-
const SIBLING_TOOL_NAMES = new Set<string>(["web-search"]);
|
|
63
|
-
|
|
64
|
-
/**
|
|
65
|
-
* Update the search status bar glyph if sibling tools are installed.
|
|
66
|
-
* Used by ``/web on|learn|off`` to keep the ``search`` slot in sync.
|
|
67
|
-
*/
|
|
68
|
-
function setSearchSlot(
|
|
69
|
-
pi: ExtensionAPI,
|
|
70
|
-
ctx: ExtensionCommandContext,
|
|
71
|
-
glyph: string,
|
|
72
|
-
): void {
|
|
73
|
-
const hasSiblingTools = getRegisteredIn(pi, SIBLING_TOOL_NAMES).length > 0;
|
|
74
|
-
if (hasSiblingTools) {
|
|
75
|
-
ctx.ui.setStatus("search", glyph);
|
|
76
|
-
}
|
|
77
|
-
}
|
|
15
|
+
// ---- Toolset specs -----------------------------------------------
|
|
16
|
+
|
|
17
|
+
const PORTAL_WEB_SPEC: ToolsetSpec = {
|
|
18
|
+
id: "portal.web",
|
|
19
|
+
names: new Set([
|
|
20
|
+
"web-fetch",
|
|
21
|
+
"browser-navigate",
|
|
22
|
+
"browser-snapshot",
|
|
23
|
+
"browser-click",
|
|
24
|
+
"browser-type",
|
|
25
|
+
"browser-scroll",
|
|
26
|
+
"browser-back",
|
|
27
|
+
"browser-press",
|
|
28
|
+
"browser-console",
|
|
29
|
+
"browser-inspect",
|
|
30
|
+
"web-guide",
|
|
31
|
+
]),
|
|
32
|
+
persistKey: "toolset-state:portal.web",
|
|
33
|
+
};
|
|
78
34
|
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
}
|
|
35
|
+
const PORTAL_LEARN_SPEC: ToolsetSpec = {
|
|
36
|
+
id: "portal.learn",
|
|
37
|
+
names: new Set(["web-learn"]),
|
|
38
|
+
persistKey: "toolset-state:portal.learn",
|
|
39
|
+
defaultEnabled: false,
|
|
40
|
+
requires: ["portal.web"],
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
// ---- Status bar cached state (derived from library events) ------
|
|
86
44
|
|
|
87
|
-
/** Last known toggle state
|
|
45
|
+
/** @internal Last known web-toggle state for status bar rendering. */
|
|
88
46
|
let _lastToggleState = true;
|
|
89
47
|
|
|
90
|
-
/** Last known learn state
|
|
48
|
+
/** @internal Last known learn state for status bar coloring. */
|
|
91
49
|
let _lastLearnState = false;
|
|
92
50
|
|
|
93
|
-
/**
|
|
94
|
-
let
|
|
51
|
+
/** @internal Last captured ExtensionContext for event-driven glyph rendering. */
|
|
52
|
+
let _lastCtx: ExtensionContext | null = null;
|
|
95
53
|
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
* if set, otherwise falls back to "none" (the router reads browser.defaultProfile
|
|
99
|
-
* from settings.json as its own fallback).
|
|
100
|
-
*/
|
|
101
|
-
export function getConversationDefaultProfile(): string | undefined {
|
|
102
|
-
return _conversationDefaultProfile;
|
|
54
|
+
export function getToggleState(): boolean {
|
|
55
|
+
return _lastToggleState;
|
|
103
56
|
}
|
|
104
57
|
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
/** Return the subset of a tool-name set that is actually registered. */
|
|
108
|
-
function getRegisteredIn(pi: ExtensionAPI, names: Set<string>): string[] {
|
|
109
|
-
return pi
|
|
110
|
-
.getAllTools()
|
|
111
|
-
.map((t) => t.name)
|
|
112
|
-
.filter((n) => names.has(n));
|
|
58
|
+
export function getLearnState(): boolean {
|
|
59
|
+
return _lastLearnState;
|
|
113
60
|
}
|
|
114
61
|
|
|
115
|
-
|
|
116
|
-
* Check whether browser tools are currently active in the system prompt.
|
|
117
|
-
*/
|
|
118
|
-
function isBrowserEnabled(pi: ExtensionAPI): boolean {
|
|
119
|
-
const registered = getRegisteredIn(pi, BROWSER_TOOL_NAMES);
|
|
62
|
+
// ---- Conversation-scoped default profile -------------------------
|
|
120
63
|
|
|
121
|
-
|
|
122
|
-
return registered.some((name) => active.has(name));
|
|
123
|
-
}
|
|
64
|
+
const PROFILE_PERSIST_KEY = "portal-conversation-state";
|
|
124
65
|
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
* @param pi ExtensionAPI
|
|
128
|
-
* @param enable true = add browser tools to the active set, false = remove them
|
|
129
|
-
*/
|
|
130
|
-
function applyBrowserState(pi: ExtensionAPI, enable: boolean): void {
|
|
131
|
-
// Combine browser tools + sibling tools into one toggle set.
|
|
132
|
-
// (/web on enables both; /web off disables both.)
|
|
133
|
-
const registered = new Set([
|
|
134
|
-
...getRegisteredIn(pi, BROWSER_TOOL_NAMES),
|
|
135
|
-
...getRegisteredIn(pi, SIBLING_TOOL_NAMES),
|
|
136
|
-
]);
|
|
137
|
-
if (registered.size === 0) return;
|
|
138
|
-
_lastToggleState = enable;
|
|
139
|
-
|
|
140
|
-
if (enable) {
|
|
141
|
-
// Merge browser+sibling tools back into whatever is currently active
|
|
142
|
-
const current = pi.getActiveTools();
|
|
143
|
-
pi.setActiveTools([...new Set([...current, ...registered])]);
|
|
144
|
-
} else {
|
|
145
|
-
// Keep everything that is NOT a browser+sibling tool
|
|
146
|
-
const all = pi.getAllTools().map((t) => t.name);
|
|
147
|
-
pi.setActiveTools(all.filter((name) => !registered.has(name)));
|
|
148
|
-
}
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
/**
|
|
152
|
-
* Persist the current toggle state into the session for branch-aware
|
|
153
|
-
* restoration across /reload, /resume, /fork, and /tree navigation.
|
|
154
|
-
*/
|
|
155
|
-
function persistState(pi: ExtensionAPI, state: BrowserToggleState): void {
|
|
156
|
-
pi.appendEntry<BrowserToggleState>("web-toggle-state", state);
|
|
66
|
+
interface ProfileState {
|
|
67
|
+
defaultProfile: string;
|
|
157
68
|
}
|
|
158
69
|
|
|
159
|
-
|
|
70
|
+
let _conversationDefaultProfile: string | undefined;
|
|
160
71
|
|
|
161
|
-
function
|
|
162
|
-
|
|
163
|
-
|
|
72
|
+
export function getConversationDefaultProfile(): string | undefined {
|
|
73
|
+
return _conversationDefaultProfile;
|
|
74
|
+
}
|
|
164
75
|
|
|
165
|
-
|
|
76
|
+
function persistProfile(pi: ExtensionAPI, profile: string): void {
|
|
77
|
+
pi.appendEntry<ProfileState>(PROFILE_PERSIST_KEY, {
|
|
78
|
+
defaultProfile: profile,
|
|
79
|
+
});
|
|
80
|
+
}
|
|
166
81
|
|
|
82
|
+
function restoreProfile(_pi: ExtensionAPI, ctx: ExtensionContext): void {
|
|
167
83
|
for (const entry of ctx.sessionManager.getBranch()) {
|
|
168
|
-
if (entry.type === "custom" && entry.customType ===
|
|
169
|
-
const data = entry.data as Record<string, unknown
|
|
170
|
-
if (data && typeof data.
|
|
171
|
-
|
|
84
|
+
if (entry.type === "custom" && entry.customType === PROFILE_PERSIST_KEY) {
|
|
85
|
+
const data = entry.data as Record<string, unknown> | undefined;
|
|
86
|
+
if (data && typeof data.defaultProfile === "string") {
|
|
87
|
+
_conversationDefaultProfile = data.defaultProfile;
|
|
88
|
+
return;
|
|
172
89
|
}
|
|
173
90
|
}
|
|
174
91
|
}
|
|
175
|
-
|
|
176
|
-
if (savedState !== undefined) {
|
|
177
|
-
applyBrowserState(pi, savedState.browserToolsEnabled);
|
|
178
|
-
applyLearnState(pi, savedState.learnToolsEnabled);
|
|
179
|
-
// Restore conversation-scoped default profile
|
|
180
|
-
if (savedState.defaultProfile && savedState.defaultProfile !== "none") {
|
|
181
|
-
_conversationDefaultProfile = savedState.defaultProfile;
|
|
182
|
-
}
|
|
183
|
-
return true;
|
|
184
|
-
}
|
|
185
|
-
|
|
186
|
-
return false;
|
|
187
|
-
}
|
|
188
|
-
|
|
189
|
-
/**
|
|
190
|
-
* Apply the config-file default on a fresh session (no branch state found).
|
|
191
|
-
* Reads `browserToggle.defaultEnabled` from pi's settings.json files.
|
|
192
|
-
* Learn always starts disabled by default.
|
|
193
|
-
*/
|
|
194
|
-
function applyConfigDefault(pi: ExtensionAPI): void {
|
|
195
|
-
const enabled = readBrowserToggleConfig();
|
|
196
|
-
applyBrowserState(pi, enabled);
|
|
197
|
-
applyLearnState(pi, false);
|
|
198
|
-
// Persist so subsequent branch navigation sees this as the initial state
|
|
199
|
-
persistState(pi, {
|
|
200
|
-
browserToolsEnabled: enabled,
|
|
201
|
-
learnToolsEnabled: false,
|
|
202
|
-
defaultProfile: "none",
|
|
203
|
-
});
|
|
204
92
|
}
|
|
205
93
|
|
|
206
|
-
// ----
|
|
207
|
-
// Helper functions are exported for testing while the default export
|
|
208
|
-
// wires the toggle into pi-lean-portal's runtime.
|
|
209
|
-
|
|
210
|
-
/**
|
|
211
|
-
* Return the last known toggle state (true = enabled, false = disabled).
|
|
212
|
-
* Returns true initially at startup (browser tools start enabled).
|
|
213
|
-
*/
|
|
214
|
-
export function getToggleState(): boolean {
|
|
215
|
-
return _lastToggleState;
|
|
216
|
-
}
|
|
217
|
-
|
|
218
|
-
/**
|
|
219
|
-
* Return the last known learn state (true = learn mode enabled).
|
|
220
|
-
* Used by the status bar to color the glyph differently.
|
|
221
|
-
*/
|
|
222
|
-
export function getLearnState(): boolean {
|
|
223
|
-
return _lastLearnState;
|
|
224
|
-
}
|
|
94
|
+
// ---- Test helpers -------------------------------------------------
|
|
225
95
|
|
|
226
|
-
/** @internal Reset
|
|
96
|
+
/** @internal Reset cached state to defaults (test helper). */
|
|
227
97
|
function _resetToggleStateForTest(): void {
|
|
228
98
|
_lastToggleState = true;
|
|
229
99
|
_lastLearnState = false;
|
|
100
|
+
_lastCtx = null;
|
|
101
|
+
_conversationDefaultProfile = undefined;
|
|
230
102
|
}
|
|
231
103
|
|
|
232
|
-
/**
|
|
233
|
-
|
|
234
|
-
*
|
|
235
|
-
* Called at the start of the extension entry function to ensure safe
|
|
236
|
-
* re-invocation when pi reuses the cached module factory (e.g.
|
|
237
|
-
* during /resume to the same working directory).
|
|
238
|
-
* Resets both the toggle status and conversation-scoped profile.
|
|
239
|
-
*/
|
|
240
|
-
export function resetToggleModuleState(): void {
|
|
104
|
+
/** @internal Reset cached state (called from index.ts on re-entry). */
|
|
105
|
+
function resetToggleModuleState(): void {
|
|
241
106
|
_lastToggleState = true;
|
|
242
107
|
_lastLearnState = false;
|
|
108
|
+
_lastCtx = null;
|
|
243
109
|
_conversationDefaultProfile = undefined;
|
|
244
110
|
}
|
|
245
111
|
|
|
246
|
-
// ----
|
|
247
|
-
|
|
248
|
-
/**
|
|
249
|
-
* Read the browser toggle default from pi's settings.json files.
|
|
250
|
-
*
|
|
251
|
-
* Looks for `browserToggle.defaultEnabled` in:
|
|
252
|
-
* 1. `~/.pi/agent/settings.json` (global)
|
|
253
|
-
* 2. `.pi/settings.json` (project-local, overrides global)
|
|
254
|
-
*
|
|
255
|
-
* Returns the config value, or `true` if unset (backward-compatible default).
|
|
256
|
-
* Silently returns `true` on missing files, parse errors, or type mismatches.
|
|
257
|
-
*/
|
|
258
|
-
function readBrowserToggleConfig(): boolean {
|
|
259
|
-
const merged = readMergedSettings();
|
|
260
|
-
const segment = merged[CONFIG_KEY];
|
|
261
|
-
|
|
262
|
-
if (
|
|
263
|
-
segment &&
|
|
264
|
-
typeof segment === "object" &&
|
|
265
|
-
!Array.isArray(segment) &&
|
|
266
|
-
typeof (segment as Record<string, unknown>).defaultEnabled === "boolean"
|
|
267
|
-
) {
|
|
268
|
-
return (segment as Record<string, unknown>).defaultEnabled as boolean;
|
|
269
|
-
}
|
|
270
|
-
|
|
271
|
-
return true; // default: enabled
|
|
272
|
-
}
|
|
273
|
-
|
|
274
|
-
/**
|
|
275
|
-
* Check whether learn tools are currently active.
|
|
276
|
-
*/
|
|
277
|
-
function isLearnEnabled(pi: ExtensionAPI): boolean {
|
|
278
|
-
const registered = getRegisteredIn(pi, LEARN_TOOL_NAMES);
|
|
279
|
-
const active = new Set(pi.getActiveTools());
|
|
280
|
-
return registered.some((name) => active.has(name));
|
|
281
|
-
}
|
|
112
|
+
// ---- Exports for testing -----------------------------------------
|
|
282
113
|
|
|
283
|
-
|
|
284
|
-
* Apply a learn-tool state without persisting.
|
|
285
|
-
* Uses getActiveTools() (not getAllTools()) to avoid accidentally activating
|
|
286
|
-
* learn tools that are not currently registered.
|
|
287
|
-
*/
|
|
288
|
-
function applyLearnState(pi: ExtensionAPI, enable: boolean): void {
|
|
289
|
-
_lastLearnState = enable;
|
|
290
|
-
const registered = new Set(getRegisteredIn(pi, LEARN_TOOL_NAMES));
|
|
291
|
-
if (registered.size === 0) return;
|
|
292
|
-
if (enable) {
|
|
293
|
-
const current = pi.getActiveTools();
|
|
294
|
-
pi.setActiveTools([...new Set([...current, ...registered])]);
|
|
295
|
-
} else {
|
|
296
|
-
// Remove learn tools from the *currently active* set
|
|
297
|
-
const current = pi.getActiveTools();
|
|
298
|
-
pi.setActiveTools(current.filter((name) => !registered.has(name)));
|
|
299
|
-
}
|
|
300
|
-
}
|
|
114
|
+
export { _resetToggleStateForTest, resetToggleModuleState };
|
|
301
115
|
|
|
302
|
-
//
|
|
303
|
-
// These are exported solely for unit testing.
|
|
304
|
-
// Do not import them directly from production code.
|
|
305
|
-
/** @internal */
|
|
306
|
-
export {
|
|
307
|
-
getRegisteredIn,
|
|
308
|
-
isBrowserEnabled,
|
|
309
|
-
applyBrowserState,
|
|
310
|
-
persistState,
|
|
311
|
-
restoreFromBranch,
|
|
312
|
-
readBrowserToggleConfig,
|
|
313
|
-
applyConfigDefault,
|
|
314
|
-
isLearnEnabled,
|
|
315
|
-
applyLearnState,
|
|
316
|
-
_resetToggleStateForTest,
|
|
317
|
-
};
|
|
318
|
-
export type { BrowserToggleState };
|
|
116
|
+
// ---- Glyph helpers -----------------------------------------------
|
|
319
117
|
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
* Set the conversation-scoped default profile.
|
|
330
|
-
* Affects this conversation only; survives /reload, /resume, /fork.
|
|
331
|
-
* "none" resets to the config-file default.
|
|
332
|
-
*/
|
|
333
|
-
function setConversationDefaultProfile(
|
|
334
|
-
pi: ExtensionAPI,
|
|
335
|
-
profile: string,
|
|
118
|
+
function renderBrowserGlyph(
|
|
119
|
+
ctx: {
|
|
120
|
+
ui: {
|
|
121
|
+
setStatus: (key: string, label: string) => void;
|
|
122
|
+
theme: { fg: (c: ThemeColor, t: string) => string };
|
|
123
|
+
};
|
|
124
|
+
},
|
|
125
|
+
webEnabled: boolean,
|
|
126
|
+
learnEnabled: boolean,
|
|
336
127
|
): void {
|
|
337
|
-
if (
|
|
338
|
-
|
|
128
|
+
if (!webEnabled) {
|
|
129
|
+
ctx.ui.setStatus("browser", "○ web off");
|
|
130
|
+
return;
|
|
131
|
+
}
|
|
132
|
+
if (learnEnabled) {
|
|
133
|
+
ctx.ui.setStatus("browser", ctx.ui.theme.fg("success", "●") + " idle");
|
|
339
134
|
} else {
|
|
340
|
-
|
|
135
|
+
ctx.ui.setStatus("browser", ctx.ui.theme.fg("accent", "●") + " idle");
|
|
341
136
|
}
|
|
342
|
-
|
|
343
|
-
// Persist so it survives /reload, /resume, /fork
|
|
344
|
-
const currentState = getCurrentState(pi);
|
|
345
|
-
persistState(pi, {
|
|
346
|
-
...currentState,
|
|
347
|
-
defaultProfile: profile,
|
|
348
|
-
});
|
|
349
137
|
}
|
|
350
138
|
|
|
351
|
-
|
|
352
|
-
function getCurrentState(
|
|
353
|
-
pi: ExtensionAPI,
|
|
354
|
-
): Pick<BrowserToggleState, "browserToolsEnabled" | "learnToolsEnabled"> {
|
|
355
|
-
return {
|
|
356
|
-
browserToolsEnabled: isBrowserEnabled(pi),
|
|
357
|
-
learnToolsEnabled: isLearnEnabled(pi),
|
|
358
|
-
};
|
|
359
|
-
}
|
|
139
|
+
// ---- Toggle initializer ------------------------------------------
|
|
360
140
|
|
|
361
|
-
// ---- Toggle initializer ----------------------------------------
|
|
362
|
-
|
|
363
|
-
/**
|
|
364
|
-
* Register the /web command and session-restoration hooks.
|
|
365
|
-
* Called from pi-lean-portal's index.ts entry point.
|
|
366
|
-
*/
|
|
367
141
|
export default function initBrowserToggle(pi: ExtensionAPI) {
|
|
368
|
-
|
|
142
|
+
const merged = readMergedSettings();
|
|
143
|
+
const browserToggleSegment = (merged as Record<string, unknown>)[
|
|
144
|
+
"browserToggle"
|
|
145
|
+
] as Record<string, unknown> | undefined;
|
|
146
|
+
const webDefault =
|
|
147
|
+
browserToggleSegment &&
|
|
148
|
+
typeof browserToggleSegment["defaultEnabled"] === "boolean"
|
|
149
|
+
? (browserToggleSegment["defaultEnabled"] as boolean)
|
|
150
|
+
: true;
|
|
151
|
+
|
|
152
|
+
const webSpec: ToolsetSpec = {
|
|
153
|
+
...PORTAL_WEB_SPEC,
|
|
154
|
+
defaultEnabled: webDefault,
|
|
155
|
+
};
|
|
156
|
+
const webToolset = defineToolset(pi, webSpec);
|
|
157
|
+
const learnToolset = defineToolset(pi, PORTAL_LEARN_SPEC);
|
|
158
|
+
|
|
159
|
+
// ── Keep cached state in sync with library events ─────────
|
|
160
|
+
// Re-render the glyph on every change/restore so external callers
|
|
161
|
+
// (e.g. pi-tbox's `/tbox all off`) keep the slot in sync — the cached
|
|
162
|
+
// flags alone don't update the status bar.
|
|
163
|
+
const syncCachedState = () => {
|
|
164
|
+
_lastToggleState = webToolset.isEnabled(pi);
|
|
165
|
+
_lastLearnState = learnToolset.isEnabled(pi);
|
|
166
|
+
if (_lastCtx) {
|
|
167
|
+
renderBrowserGlyph(_lastCtx, _lastToggleState, _lastLearnState);
|
|
168
|
+
}
|
|
169
|
+
};
|
|
369
170
|
|
|
171
|
+
pi.events.on(TOOLSET_EVENTS.changed, syncCachedState);
|
|
172
|
+
pi.events.on(TOOLSET_EVENTS.restored, syncCachedState);
|
|
173
|
+
|
|
174
|
+
// ── /web command ──────────────────────────────────────────
|
|
370
175
|
pi.registerCommand("web", {
|
|
371
176
|
description:
|
|
372
177
|
"Enable/disable browser automation tools. " +
|
|
373
178
|
"Usage: /web on | off | learn | status",
|
|
374
179
|
handler: async (args, ctx) => {
|
|
375
180
|
const cmd = args.trim().toLowerCase();
|
|
376
|
-
const hasBrowserTools = getRegisteredIn(pi, BROWSER_TOOL_NAMES).length > 0;
|
|
377
181
|
|
|
378
|
-
|
|
182
|
+
// Focus-mode guard (§13.2): refuse actuating subcommands while the
|
|
183
|
+
// library holds the line in inclusion mode, so a sibling toggle
|
|
184
|
+
// can't write a focus-indistinguishable {enabled} entry. Read-only
|
|
185
|
+
// subcommands (status/profile/cookies/bare /web) stay unguarded,
|
|
186
|
+
// matching tbox's treatment of its own read-only commands.
|
|
187
|
+
if (
|
|
188
|
+
["on", "off", "learn"].includes(cmd) &&
|
|
189
|
+
getDefaultResolutionMode() === "inclusion"
|
|
190
|
+
) {
|
|
379
191
|
ctx.ui.notify(
|
|
380
|
-
"
|
|
192
|
+
"Another plugin has active inclusion mode — this toolset can't be toggled while inclusion is holding the line. Deactivate it there first.",
|
|
381
193
|
"warning",
|
|
382
194
|
);
|
|
383
195
|
return;
|
|
384
196
|
}
|
|
385
197
|
|
|
386
198
|
if (cmd === "on") {
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
persistState(pi, {
|
|
390
|
-
browserToolsEnabled: true,
|
|
391
|
-
learnToolsEnabled: false,
|
|
392
|
-
defaultProfile: _conversationDefaultProfile ?? "none",
|
|
393
|
-
});
|
|
394
|
-
ctx.ui.setStatus("browser", ctx.ui.theme.fg("accent", "●") + " idle");
|
|
395
|
-
|
|
396
|
-
setSearchSlot(pi, ctx, ctx.ui.theme.fg("accent", "●") + " searxng");
|
|
397
|
-
|
|
199
|
+
webToolset.enable(pi);
|
|
200
|
+
learnToolset.disable(pi);
|
|
398
201
|
ctx.ui.notify(
|
|
399
202
|
"🌐 Browser tools enabled. /web learn to make web-learn available.",
|
|
400
203
|
"info",
|
|
401
204
|
);
|
|
402
205
|
} else if (cmd === "learn") {
|
|
403
|
-
|
|
404
|
-
applyLearnState(pi, true);
|
|
405
|
-
persistState(pi, {
|
|
406
|
-
browserToolsEnabled: true,
|
|
407
|
-
learnToolsEnabled: true,
|
|
408
|
-
defaultProfile: _conversationDefaultProfile ?? "none",
|
|
409
|
-
});
|
|
410
|
-
ctx.ui.setStatus("browser", ctx.ui.theme.fg("success", "●") + " idle");
|
|
411
|
-
|
|
412
|
-
setSearchSlot(pi, ctx, ctx.ui.theme.fg("accent", "●") + " searxng");
|
|
413
|
-
|
|
206
|
+
learnToolset.enable(pi); // cascades web on via requires
|
|
414
207
|
ctx.ui.notify(
|
|
415
208
|
"📖 web-learn tool is now available. Agent will save/update guides when asked.",
|
|
416
209
|
"info",
|
|
417
210
|
);
|
|
418
211
|
} else if (cmd === "off") {
|
|
419
|
-
|
|
420
|
-
applyLearnState(pi, false);
|
|
421
|
-
persistState(pi, {
|
|
422
|
-
browserToolsEnabled: false,
|
|
423
|
-
learnToolsEnabled: false,
|
|
424
|
-
defaultProfile: _conversationDefaultProfile ?? "none",
|
|
425
|
-
});
|
|
426
|
-
ctx.ui.setStatus("browser", "○ web off");
|
|
427
|
-
|
|
428
|
-
setSearchSlot(pi, ctx, "○ searxng");
|
|
429
|
-
|
|
212
|
+
webToolset.disable(pi); // cascades learn off via requires
|
|
430
213
|
ctx.ui.notify(
|
|
431
214
|
"🌐 Browser tools disabled. /web on to re-enable.",
|
|
432
215
|
"info",
|
|
433
216
|
);
|
|
434
217
|
} else if (cmd === "profile" || cmd.startsWith("profile ")) {
|
|
435
218
|
const sub = cmd.slice("profile".length).trim();
|
|
219
|
+
const { handleProfileSubcommand } = await import(
|
|
220
|
+
"./browser-profile.js"
|
|
221
|
+
);
|
|
436
222
|
await handleProfileSubcommand(sub, ctx, pi, (profile: string) => {
|
|
437
|
-
|
|
223
|
+
if (profile === "none") {
|
|
224
|
+
_conversationDefaultProfile = undefined;
|
|
225
|
+
} else {
|
|
226
|
+
_conversationDefaultProfile = profile;
|
|
227
|
+
}
|
|
228
|
+
persistProfile(pi, profile);
|
|
438
229
|
});
|
|
439
230
|
} else if (cmd === "cookies" || cmd.startsWith("cookies ")) {
|
|
440
231
|
const sub = cmd.slice("cookies".length).trim();
|
|
232
|
+
const { handleCookiesSubcommand } = await import(
|
|
233
|
+
"./browser-cookies.js"
|
|
234
|
+
);
|
|
441
235
|
await handleCookiesSubcommand(sub, ctx);
|
|
442
236
|
} else if (cmd === "status") {
|
|
443
|
-
handleStatusSubcommand
|
|
237
|
+
const { handleStatusSubcommand } = await import("./browser-status.js");
|
|
238
|
+
handleStatusSubcommand(
|
|
239
|
+
ctx,
|
|
240
|
+
webToolset.isEnabled(pi),
|
|
241
|
+
learnToolset.isEnabled(pi),
|
|
242
|
+
);
|
|
444
243
|
} else {
|
|
445
244
|
// Default: show status
|
|
446
|
-
const
|
|
447
|
-
const
|
|
245
|
+
const webOn = webToolset.isEnabled(pi) ? "✅ on" : "❌ off";
|
|
246
|
+
const learnOn = learnToolset.isEnabled(pi) ? "✅ on" : "❌ off";
|
|
448
247
|
ctx.ui.notify(
|
|
449
|
-
`🌐 Browser tools: ${
|
|
450
|
-
`📖 Learn mode: ${
|
|
248
|
+
`🌐 Browser tools: ${webOn}\n` +
|
|
249
|
+
`📖 Learn mode: ${learnOn}\n` +
|
|
451
250
|
` /web profile manage browser profiles\n` +
|
|
452
251
|
` /web cookies inspect or clear session cookies\n` +
|
|
453
252
|
` /web off disable all browser tools\n` +
|
|
@@ -461,31 +260,20 @@ export default function initBrowserToggle(pi: ExtensionAPI) {
|
|
|
461
260
|
},
|
|
462
261
|
});
|
|
463
262
|
|
|
464
|
-
// ──
|
|
465
|
-
// Ensures the toggle survives /reload, /resume, /fork, and
|
|
466
|
-
// /tree navigation without "forgetting" whether we're on or off.
|
|
467
|
-
//
|
|
468
|
-
// For fresh sessions (no branch state), falls back to the config
|
|
469
|
-
// default from settings.json (`browserToggle.defaultEnabled`).
|
|
470
|
-
|
|
263
|
+
// ── Session handlers: restore profile + render glyph ─────
|
|
471
264
|
pi.on("session_start", async (_event, ctx) => {
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
applyConfigDefault(pi);
|
|
476
|
-
}
|
|
477
|
-
|
|
478
|
-
// After state restoration, ensure the search slot reflects the actual
|
|
479
|
-
// toggle state. Without this, a race between portal's session_start
|
|
480
|
-
// (async — restores/applies state) and search's session_start (probes
|
|
481
|
-
// health) can leave the search glyph showing blue when tools are off.
|
|
482
|
-
const hasSiblingTools = getRegisteredIn(pi, SIBLING_TOOL_NAMES).length > 0;
|
|
483
|
-
if (hasSiblingTools && !isBrowserEnabled(pi)) {
|
|
484
|
-
ctx.ui.setStatus("search", "○ searxng");
|
|
485
|
-
}
|
|
265
|
+
restoreProfile(pi, ctx);
|
|
266
|
+
_lastCtx = ctx;
|
|
267
|
+
syncCachedState();
|
|
486
268
|
});
|
|
487
269
|
|
|
488
270
|
pi.on("session_tree", async (_event, ctx) => {
|
|
489
|
-
|
|
271
|
+
restoreProfile(pi, ctx);
|
|
272
|
+
_lastCtx = ctx;
|
|
273
|
+
syncCachedState();
|
|
274
|
+
});
|
|
275
|
+
|
|
276
|
+
pi.on("session_shutdown", async () => {
|
|
277
|
+
_lastCtx = null;
|
|
490
278
|
});
|
|
491
279
|
}
|
|
@@ -118,8 +118,8 @@ rather than failing opaquely.
|
|
|
118
118
|
executes stealth backends automatically. `user-backends/` is trusted
|
|
119
119
|
user code — you wrote or audited it.
|
|
120
120
|
- **Not shipped in the npm tarball.** The templates live in the source
|
|
121
|
-
|
|
122
|
-
|
|
121
|
+
repo under `contributed/`; `contributed/` is not included in the
|
|
122
|
+
`package.json` `files` allow-list. You need the git repo (or a copy of the
|
|
123
123
|
files) to install a stealth backend.
|
|
124
124
|
- **Not in the default fallback list.** Stealth backends are loaded
|
|
125
125
|
only when explicitly listed in `browser.plugins`. A fresh install
|