@mseep/clawdcursor 1.5.5
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/CHANGELOG.md +2264 -0
- package/LICENSE +21 -0
- package/README.md +385 -0
- package/SECURITY.md +44 -0
- package/SKILL.md +503 -0
- package/dist/core/agent-loop/agent.d.ts +42 -0
- package/dist/core/agent-loop/agent.js +1023 -0
- package/dist/core/agent-loop/agent.js.map +1 -0
- package/dist/core/agent-loop/batch-tool.d.ts +25 -0
- package/dist/core/agent-loop/batch-tool.js +218 -0
- package/dist/core/agent-loop/batch-tool.js.map +1 -0
- package/dist/core/agent-loop/coord-scale.d.ts +72 -0
- package/dist/core/agent-loop/coord-scale.js +89 -0
- package/dist/core/agent-loop/coord-scale.js.map +1 -0
- package/dist/core/agent-loop/focus-guard.d.ts +24 -0
- package/dist/core/agent-loop/focus-guard.js +29 -0
- package/dist/core/agent-loop/focus-guard.js.map +1 -0
- package/dist/core/agent-loop/project-mcp.d.ts +97 -0
- package/dist/core/agent-loop/project-mcp.js +253 -0
- package/dist/core/agent-loop/project-mcp.js.map +1 -0
- package/dist/core/agent-loop/prompt.d.ts +45 -0
- package/dist/core/agent-loop/prompt.js +426 -0
- package/dist/core/agent-loop/prompt.js.map +1 -0
- package/dist/core/agent-loop/tool-meta.d.ts +93 -0
- package/dist/core/agent-loop/tool-meta.js +651 -0
- package/dist/core/agent-loop/tool-meta.js.map +1 -0
- package/dist/core/agent-loop/tools.d.ts +38 -0
- package/dist/core/agent-loop/tools.js +2134 -0
- package/dist/core/agent-loop/tools.js.map +1 -0
- package/dist/core/agent-loop/types.d.ts +170 -0
- package/dist/core/agent-loop/types.js +12 -0
- package/dist/core/agent-loop/types.js.map +1 -0
- package/dist/core/agent.d.ts +51 -0
- package/dist/core/agent.js +245 -0
- package/dist/core/agent.js.map +1 -0
- package/dist/core/app-categories.d.ts +67 -0
- package/dist/core/app-categories.js +108 -0
- package/dist/core/app-categories.js.map +1 -0
- package/dist/core/banner.d.ts +70 -0
- package/dist/core/banner.js +245 -0
- package/dist/core/banner.js.map +1 -0
- package/dist/core/classify/capability.d.ts +45 -0
- package/dist/core/classify/capability.js +78 -0
- package/dist/core/classify/capability.js.map +1 -0
- package/dist/core/decompose/llm-decomposer.d.ts +35 -0
- package/dist/core/decompose/llm-decomposer.js +156 -0
- package/dist/core/decompose/llm-decomposer.js.map +1 -0
- package/dist/core/decompose/parser.d.ts +27 -0
- package/dist/core/decompose/parser.js +101 -0
- package/dist/core/decompose/parser.js.map +1 -0
- package/dist/core/observability/correlation.d.ts +19 -0
- package/dist/core/observability/correlation.js +36 -0
- package/dist/core/observability/correlation.js.map +1 -0
- package/dist/core/observability/cost-meter.d.ts +51 -0
- package/dist/core/observability/cost-meter.js +134 -0
- package/dist/core/observability/cost-meter.js.map +1 -0
- package/dist/core/observability/logger.d.ts +61 -0
- package/dist/core/observability/logger.js +550 -0
- package/dist/core/observability/logger.js.map +1 -0
- package/dist/core/router/aliases.d.ts +50 -0
- package/dist/core/router/aliases.js +104 -0
- package/dist/core/router/aliases.js.map +1 -0
- package/dist/core/router/normalize.d.ts +41 -0
- package/dist/core/router/normalize.js +80 -0
- package/dist/core/router/normalize.js.map +1 -0
- package/dist/core/safety.d.ts +126 -0
- package/dist/core/safety.js +568 -0
- package/dist/core/safety.js.map +1 -0
- package/dist/core/sense/a11y-resolver.d.ts +73 -0
- package/dist/core/sense/a11y-resolver.js +76 -0
- package/dist/core/sense/a11y-resolver.js.map +1 -0
- package/dist/core/sense/fingerprint.d.ts +41 -0
- package/dist/core/sense/fingerprint.js +123 -0
- package/dist/core/sense/fingerprint.js.map +1 -0
- package/dist/core/sense/rank.d.ts +70 -0
- package/dist/core/sense/rank.js +192 -0
- package/dist/core/sense/rank.js.map +1 -0
- package/dist/core/sense/reactive-check.d.ts +40 -0
- package/dist/core/sense/reactive-check.js +48 -0
- package/dist/core/sense/reactive-check.js.map +1 -0
- package/dist/core/sense/snapshot.d.ts +19 -0
- package/dist/core/sense/snapshot.js +100 -0
- package/dist/core/sense/snapshot.js.map +1 -0
- package/dist/core/sense/types.d.ts +66 -0
- package/dist/core/sense/types.js +9 -0
- package/dist/core/sense/types.js.map +1 -0
- package/dist/core/sense/ui-map-anchors.d.ts +7 -0
- package/dist/core/sense/ui-map-anchors.js +24 -0
- package/dist/core/sense/ui-map-anchors.js.map +1 -0
- package/dist/core/sense/ui-map-elements.d.ts +5 -0
- package/dist/core/sense/ui-map-elements.js +33 -0
- package/dist/core/sense/ui-map-elements.js.map +1 -0
- package/dist/core/sense/ui-map-find.d.ts +56 -0
- package/dist/core/sense/ui-map-find.js +153 -0
- package/dist/core/sense/ui-map-find.js.map +1 -0
- package/dist/core/sense/ui-map-fuse.d.ts +4 -0
- package/dist/core/sense/ui-map-fuse.js +44 -0
- package/dist/core/sense/ui-map-fuse.js.map +1 -0
- package/dist/core/sense/ui-map-geom.d.ts +3 -0
- package/dist/core/sense/ui-map-geom.js +16 -0
- package/dist/core/sense/ui-map-geom.js.map +1 -0
- package/dist/core/sense/ui-map-holder.d.ts +58 -0
- package/dist/core/sense/ui-map-holder.js +87 -0
- package/dist/core/sense/ui-map-holder.js.map +1 -0
- package/dist/core/sense/ui-map-normalize.d.ts +19 -0
- package/dist/core/sense/ui-map-normalize.js +65 -0
- package/dist/core/sense/ui-map-normalize.js.map +1 -0
- package/dist/core/sense/ui-map-render.d.ts +4 -0
- package/dist/core/sense/ui-map-render.js +34 -0
- package/dist/core/sense/ui-map-render.js.map +1 -0
- package/dist/core/sense/ui-map-resolve.d.ts +41 -0
- package/dist/core/sense/ui-map-resolve.js +59 -0
- package/dist/core/sense/ui-map-resolve.js.map +1 -0
- package/dist/core/sense/ui-map-types.d.ts +66 -0
- package/dist/core/sense/ui-map-types.js +11 -0
- package/dist/core/sense/ui-map-types.js.map +1 -0
- package/dist/core/sense/ui-map.d.ts +29 -0
- package/dist/core/sense/ui-map.js +113 -0
- package/dist/core/sense/ui-map.js.map +1 -0
- package/dist/core/verify/assertions.d.ts +132 -0
- package/dist/core/verify/assertions.js +284 -0
- package/dist/core/verify/assertions.js.map +1 -0
- package/dist/index.d.ts +21 -0
- package/dist/index.js +24 -0
- package/dist/index.js.map +1 -0
- package/dist/llm/browser-config.d.ts +36 -0
- package/dist/llm/browser-config.js +83 -0
- package/dist/llm/browser-config.js.map +1 -0
- package/dist/llm/client.d.ts +268 -0
- package/dist/llm/client.js +1094 -0
- package/dist/llm/client.js.map +1 -0
- package/dist/llm/config.d.ts +79 -0
- package/dist/llm/config.js +375 -0
- package/dist/llm/config.js.map +1 -0
- package/dist/llm/credentials.d.ts +35 -0
- package/dist/llm/credentials.js +491 -0
- package/dist/llm/credentials.js.map +1 -0
- package/dist/llm/external-creds.d.ts +42 -0
- package/dist/llm/external-creds.js +169 -0
- package/dist/llm/external-creds.js.map +1 -0
- package/dist/llm/providers.d.ts +123 -0
- package/dist/llm/providers.js +717 -0
- package/dist/llm/providers.js.map +1 -0
- package/dist/paths.d.ts +31 -0
- package/dist/paths.js +147 -0
- package/dist/paths.js.map +1 -0
- package/dist/platform/accessibility.d.ts +139 -0
- package/dist/platform/accessibility.js +670 -0
- package/dist/platform/accessibility.js.map +1 -0
- package/dist/platform/cdp-driver.d.ts +318 -0
- package/dist/platform/cdp-driver.js +1179 -0
- package/dist/platform/cdp-driver.js.map +1 -0
- package/dist/platform/index.d.ts +11 -0
- package/dist/platform/index.js +69 -0
- package/dist/platform/index.js.map +1 -0
- package/dist/platform/keys.d.ts +17 -0
- package/dist/platform/keys.js +129 -0
- package/dist/platform/keys.js.map +1 -0
- package/dist/platform/launch-poll.d.ts +101 -0
- package/dist/platform/launch-poll.js +177 -0
- package/dist/platform/launch-poll.js.map +1 -0
- package/dist/platform/linux.d.ts +173 -0
- package/dist/platform/linux.js +1253 -0
- package/dist/platform/linux.js.map +1 -0
- package/dist/platform/macos.d.ts +136 -0
- package/dist/platform/macos.js +976 -0
- package/dist/platform/macos.js.map +1 -0
- package/dist/platform/native-desktop.d.ts +145 -0
- package/dist/platform/native-desktop.js +936 -0
- package/dist/platform/native-desktop.js.map +1 -0
- package/dist/platform/native-helper.d.ts +130 -0
- package/dist/platform/native-helper.js +592 -0
- package/dist/platform/native-helper.js.map +1 -0
- package/dist/platform/ocr-engine.d.ts +78 -0
- package/dist/platform/ocr-engine.js +363 -0
- package/dist/platform/ocr-engine.js.map +1 -0
- package/dist/platform/ps-runner.d.ts +28 -0
- package/dist/platform/ps-runner.js +228 -0
- package/dist/platform/ps-runner.js.map +1 -0
- package/dist/platform/types.d.ts +397 -0
- package/dist/platform/types.js +15 -0
- package/dist/platform/types.js.map +1 -0
- package/dist/platform/uri-handler.d.ts +75 -0
- package/dist/platform/uri-handler.js +273 -0
- package/dist/platform/uri-handler.js.map +1 -0
- package/dist/platform/wayland-backend.d.ts +53 -0
- package/dist/platform/wayland-backend.js +348 -0
- package/dist/platform/wayland-backend.js.map +1 -0
- package/dist/platform/windows.d.ts +232 -0
- package/dist/platform/windows.js +1210 -0
- package/dist/platform/windows.js.map +1 -0
- package/dist/postbuild.d.ts +10 -0
- package/dist/postbuild.js +98 -0
- package/dist/postbuild.js.map +1 -0
- package/dist/schema/snapshot.d.ts +33 -0
- package/dist/schema/snapshot.js +90 -0
- package/dist/schema/snapshot.js.map +1 -0
- package/dist/shortcuts.d.ts +30 -0
- package/dist/shortcuts.js +261 -0
- package/dist/shortcuts.js.map +1 -0
- package/dist/surface/cli.d.ts +7 -0
- package/dist/surface/cli.js +1556 -0
- package/dist/surface/cli.js.map +1 -0
- package/dist/surface/dashboard.d.ts +8 -0
- package/dist/surface/dashboard.js +1193 -0
- package/dist/surface/dashboard.js.map +1 -0
- package/dist/surface/doctor.d.ts +29 -0
- package/dist/surface/doctor.js +1514 -0
- package/dist/surface/doctor.js.map +1 -0
- package/dist/surface/format.d.ts +10 -0
- package/dist/surface/format.js +37 -0
- package/dist/surface/format.js.map +1 -0
- package/dist/surface/http-utility.d.ts +65 -0
- package/dist/surface/http-utility.js +336 -0
- package/dist/surface/http-utility.js.map +1 -0
- package/dist/surface/mcp-server.d.ts +91 -0
- package/dist/surface/mcp-server.js +280 -0
- package/dist/surface/mcp-server.js.map +1 -0
- package/dist/surface/onboarding.d.ts +15 -0
- package/dist/surface/onboarding.js +184 -0
- package/dist/surface/onboarding.js.map +1 -0
- package/dist/surface/pidfile.d.ts +79 -0
- package/dist/surface/pidfile.js +263 -0
- package/dist/surface/pidfile.js.map +1 -0
- package/dist/surface/readiness.d.ts +45 -0
- package/dist/surface/readiness.js +230 -0
- package/dist/surface/readiness.js.map +1 -0
- package/dist/surface/report.d.ts +68 -0
- package/dist/surface/report.js +341 -0
- package/dist/surface/report.js.map +1 -0
- package/dist/surface/skill-register.d.ts +14 -0
- package/dist/surface/skill-register.js +150 -0
- package/dist/surface/skill-register.js.map +1 -0
- package/dist/surface/version.d.ts +6 -0
- package/dist/surface/version.js +27 -0
- package/dist/surface/version.js.map +1 -0
- package/dist/tools/a11y.d.ts +8 -0
- package/dist/tools/a11y.js +545 -0
- package/dist/tools/a11y.js.map +1 -0
- package/dist/tools/a11y_depth.d.ts +19 -0
- package/dist/tools/a11y_depth.js +455 -0
- package/dist/tools/a11y_depth.js.map +1 -0
- package/dist/tools/agent.d.ts +15 -0
- package/dist/tools/agent.js +248 -0
- package/dist/tools/agent.js.map +1 -0
- package/dist/tools/batch.d.ts +46 -0
- package/dist/tools/batch.js +230 -0
- package/dist/tools/batch.js.map +1 -0
- package/dist/tools/cdp.d.ts +8 -0
- package/dist/tools/cdp.js +233 -0
- package/dist/tools/cdp.js.map +1 -0
- package/dist/tools/compact.d.ts +63 -0
- package/dist/tools/compact.js +418 -0
- package/dist/tools/compact.js.map +1 -0
- package/dist/tools/cost-class.d.ts +38 -0
- package/dist/tools/cost-class.js +117 -0
- package/dist/tools/cost-class.js.map +1 -0
- package/dist/tools/desktop.d.ts +9 -0
- package/dist/tools/desktop.js +346 -0
- package/dist/tools/desktop.js.map +1 -0
- package/dist/tools/electron_bridge.d.ts +41 -0
- package/dist/tools/electron_bridge.js +261 -0
- package/dist/tools/electron_bridge.js.map +1 -0
- package/dist/tools/extras.d.ts +22 -0
- package/dist/tools/extras.js +942 -0
- package/dist/tools/extras.js.map +1 -0
- package/dist/tools/favorites.d.ts +13 -0
- package/dist/tools/favorites.js +137 -0
- package/dist/tools/favorites.js.map +1 -0
- package/dist/tools/introspection.d.ts +13 -0
- package/dist/tools/introspection.js +55 -0
- package/dist/tools/introspection.js.map +1 -0
- package/dist/tools/ocr.d.ts +8 -0
- package/dist/tools/ocr.js +66 -0
- package/dist/tools/ocr.js.map +1 -0
- package/dist/tools/orchestration.d.ts +7 -0
- package/dist/tools/orchestration.js +377 -0
- package/dist/tools/orchestration.js.map +1 -0
- package/dist/tools/playbooks/extract-compose.d.ts +22 -0
- package/dist/tools/playbooks/extract-compose.js +85 -0
- package/dist/tools/playbooks/extract-compose.js.map +1 -0
- package/dist/tools/playbooks/find-replace.d.ts +11 -0
- package/dist/tools/playbooks/find-replace.js +56 -0
- package/dist/tools/playbooks/find-replace.js.map +1 -0
- package/dist/tools/playbooks/index.d.ts +63 -0
- package/dist/tools/playbooks/index.js +70 -0
- package/dist/tools/playbooks/index.js.map +1 -0
- package/dist/tools/playbooks/keys-blocklist.d.ts +24 -0
- package/dist/tools/playbooks/keys-blocklist.js +89 -0
- package/dist/tools/playbooks/keys-blocklist.js.map +1 -0
- package/dist/tools/registry.d.ts +40 -0
- package/dist/tools/registry.js +560 -0
- package/dist/tools/registry.js.map +1 -0
- package/dist/tools/safety-gate.d.ts +16 -0
- package/dist/tools/safety-gate.js +70 -0
- package/dist/tools/safety-gate.js.map +1 -0
- package/dist/tools/scheduler.d.ts +76 -0
- package/dist/tools/scheduler.js +413 -0
- package/dist/tools/scheduler.js.map +1 -0
- package/dist/tools/shortcuts.d.ts +13 -0
- package/dist/tools/shortcuts.js +205 -0
- package/dist/tools/shortcuts.js.map +1 -0
- package/dist/tools/smart.d.ts +15 -0
- package/dist/tools/smart.js +785 -0
- package/dist/tools/smart.js.map +1 -0
- package/dist/tools/types.d.ts +174 -0
- package/dist/tools/types.js +67 -0
- package/dist/tools/types.js.map +1 -0
- package/dist/tools/window-text.d.ts +15 -0
- package/dist/tools/window-text.js +39 -0
- package/dist/tools/window-text.js.map +1 -0
- package/dist/types.d.ts +122 -0
- package/dist/types.js +41 -0
- package/dist/types.js.map +1 -0
- package/native/Package.swift +38 -0
- package/native/README.md +113 -0
- package/native/Sources/ClawdCursorHelper/main.swift +602 -0
- package/native/Sources/ClawdCursorHost/main.swift +182 -0
- package/native/Sources/PermissionCheck/main.swift +53 -0
- package/native/Sources/ScreenshotHelper/main.swift +219 -0
- package/native/build.sh +139 -0
- package/native/entitlements.plist +12 -0
- package/package.json +115 -0
- package/scripts/banner.ps1 +112 -0
- package/scripts/coord-accuracy.ps1 +140 -0
- package/scripts/coord-uwp.ps1 +80 -0
- package/scripts/edge-glow.ps1 +180 -0
- package/scripts/find-element.ps1 +198 -0
- package/scripts/get-foreground-window.ps1 +71 -0
- package/scripts/get-screen-context.ps1 +183 -0
- package/scripts/get-windows.ps1 +66 -0
- package/scripts/install-panic-hotkey.ps1 +46 -0
- package/scripts/interact-element.ps1 +431 -0
- package/scripts/invoke-element.ps1 +314 -0
- package/scripts/linux/atspi-bridge.py +356 -0
- package/scripts/linux/ocr-recognize.py +154 -0
- package/scripts/mac/_window-picker.jxa +163 -0
- package/scripts/mac/find-element.jxa +0 -0
- package/scripts/mac/find-element.sh +161 -0
- package/scripts/mac/focus-window.jxa +284 -0
- package/scripts/mac/get-focused-element.jxa +102 -0
- package/scripts/mac/get-foreground-window.jxa +173 -0
- package/scripts/mac/get-screen-context.jxa +197 -0
- package/scripts/mac/get-ui-tree.sh +141 -0
- package/scripts/mac/get-windows.jxa +117 -0
- package/scripts/mac/interact-element.sh +235 -0
- package/scripts/mac/invoke-element.jxa +408 -0
- package/scripts/mac/ocr-recognize.swift +124 -0
- package/scripts/ocr-recognize.ps1 +102 -0
- package/scripts/postinstall-native.js +48 -0
- package/scripts/ps-bridge.ps1 +830 -0
- package/scripts/smoke-mcp.ps1 +119 -0
- package/scripts/sync-version.ts +178 -0
- package/scripts/verify-install.js +81 -0
|
@@ -0,0 +1,245 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Control banner — the on-screen "desktop control in progress" indicators:
|
|
4
|
+
* - scripts/banner.ps1: topmost, no-activate pill with a blinking red dot;
|
|
5
|
+
* carries the double-click → stop affordance.
|
|
6
|
+
* - scripts/edge-glow.ps1: a full-screen, click-through amber glow that pulses
|
|
7
|
+
* on all four screen edges. Pure ambient signal; spawned/killed in lockstep
|
|
8
|
+
* with the pill so the two are always in sync. Opt out via CLAWD_NO_GLOW=1.
|
|
9
|
+
*
|
|
10
|
+
* Transparency contract: whenever an agent is actively driving this desktop,
|
|
11
|
+
* a human at the machine sees it — and can kill it (double-click → the parent
|
|
12
|
+
* runs the `clawdcursor stop` flow). Two lifecycles feed it:
|
|
13
|
+
*
|
|
14
|
+
* - pin()/unpin(): the autonomous task loop pins the banner for the whole
|
|
15
|
+
* task (agent.executeTask). A pinned banner ignores idle timers.
|
|
16
|
+
* - touch(): every consequential (safetyTier ≥ 1) MCP tool call pokes the
|
|
17
|
+
* banner; it shows on the first poke and auto-hides after IDLE_HIDE_MS of
|
|
18
|
+
* inactivity. This covers EXTERNAL agents (editor-hosted over stdio or
|
|
19
|
+
* HTTP /mcp) where there is no task boundary to hook.
|
|
20
|
+
*
|
|
21
|
+
* Windows-only today (WinForms via the warm PowerShell host); macOS/Linux are
|
|
22
|
+
* silent no-ops — the API is platform-neutral so adapters can land later.
|
|
23
|
+
* Disable via `--no-banner` or CLAWD_NO_BANNER=1.
|
|
24
|
+
*/
|
|
25
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
26
|
+
if (k2 === undefined) k2 = k;
|
|
27
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
28
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
29
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
30
|
+
}
|
|
31
|
+
Object.defineProperty(o, k2, desc);
|
|
32
|
+
}) : (function(o, m, k, k2) {
|
|
33
|
+
if (k2 === undefined) k2 = k;
|
|
34
|
+
o[k2] = m[k];
|
|
35
|
+
}));
|
|
36
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
37
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
38
|
+
}) : function(o, v) {
|
|
39
|
+
o["default"] = v;
|
|
40
|
+
});
|
|
41
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
42
|
+
var ownKeys = function(o) {
|
|
43
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
44
|
+
var ar = [];
|
|
45
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
46
|
+
return ar;
|
|
47
|
+
};
|
|
48
|
+
return ownKeys(o);
|
|
49
|
+
};
|
|
50
|
+
return function (mod) {
|
|
51
|
+
if (mod && mod.__esModule) return mod;
|
|
52
|
+
var result = {};
|
|
53
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
54
|
+
__setModuleDefault(result, mod);
|
|
55
|
+
return result;
|
|
56
|
+
};
|
|
57
|
+
})();
|
|
58
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
59
|
+
exports.controlBanner = void 0;
|
|
60
|
+
const child_process_1 = require("child_process");
|
|
61
|
+
const path = __importStar(require("path"));
|
|
62
|
+
const readline = __importStar(require("readline"));
|
|
63
|
+
const paths_1 = require("../paths");
|
|
64
|
+
const IDLE_HIDE_MS = 30_000;
|
|
65
|
+
class ControlBanner {
|
|
66
|
+
child = null; // the pill (carries BANNER_STOP)
|
|
67
|
+
glowChild = null; // the screen-edge glow overlay
|
|
68
|
+
idleTimer = null;
|
|
69
|
+
pinned = false;
|
|
70
|
+
enabled = process.platform === 'win32' && process.env.CLAWD_NO_BANNER !== '1';
|
|
71
|
+
// The edge glow rides the same lifecycle as the pill but is independently
|
|
72
|
+
// opt-out-able (some users find ambient pulsing distracting) without losing
|
|
73
|
+
// the pill's double-click-to-stop affordance.
|
|
74
|
+
glowEnabled = process.env.CLAWD_NO_GLOW !== '1';
|
|
75
|
+
onStopRequested;
|
|
76
|
+
spawner = () => {
|
|
77
|
+
const script = path.join((0, paths_1.getPackageRoot)(), 'scripts', 'banner.ps1');
|
|
78
|
+
return (0, child_process_1.spawn)('powershell.exe', ['-NoProfile', '-ExecutionPolicy', 'Bypass', '-WindowStyle', 'Hidden', '-File', script], { stdio: ['ignore', 'pipe', 'ignore'] });
|
|
79
|
+
};
|
|
80
|
+
glowSpawner = () => {
|
|
81
|
+
const script = path.join((0, paths_1.getPackageRoot)(), 'scripts', 'edge-glow.ps1');
|
|
82
|
+
return (0, child_process_1.spawn)('powershell.exe', ['-NoProfile', '-ExecutionPolicy', 'Bypass', '-WindowStyle', 'Hidden', '-File', script], { stdio: ['ignore', 'ignore', 'ignore'] }); // click-through visual only — no stdout contract
|
|
83
|
+
};
|
|
84
|
+
/** Wire the double-click → stop callback (the `clawdcursor stop` flow). */
|
|
85
|
+
configure(opts) {
|
|
86
|
+
this.onStopRequested = opts.onStopRequested;
|
|
87
|
+
}
|
|
88
|
+
/** Hard enable/disable (CLI --no-banner / config). Disabling hides immediately. */
|
|
89
|
+
setEnabled(v) {
|
|
90
|
+
this.enabled = v && process.platform === 'win32' && process.env.CLAWD_NO_BANNER !== '1';
|
|
91
|
+
if (!this.enabled)
|
|
92
|
+
this.forceHide();
|
|
93
|
+
}
|
|
94
|
+
isVisible() {
|
|
95
|
+
return this.child !== null;
|
|
96
|
+
}
|
|
97
|
+
/** Show and keep shown until unpin() — the autonomous-task lifecycle. */
|
|
98
|
+
pin() {
|
|
99
|
+
if (!this.enabled)
|
|
100
|
+
return;
|
|
101
|
+
this.pinned = true;
|
|
102
|
+
this.clearIdleTimer();
|
|
103
|
+
this.show();
|
|
104
|
+
}
|
|
105
|
+
/** Release the task pin; hides unless tool activity re-touches it. */
|
|
106
|
+
unpin() {
|
|
107
|
+
this.pinned = false;
|
|
108
|
+
this.forceHide();
|
|
109
|
+
}
|
|
110
|
+
/**
|
|
111
|
+
* Activity poke from a consequential tool call: show now, auto-hide after
|
|
112
|
+
* IDLE_HIDE_MS without further pokes. No-op on the timer while pinned.
|
|
113
|
+
*/
|
|
114
|
+
touch(idleMs = IDLE_HIDE_MS) {
|
|
115
|
+
if (!this.enabled)
|
|
116
|
+
return;
|
|
117
|
+
this.show();
|
|
118
|
+
if (this.pinned)
|
|
119
|
+
return; // task lifecycle owns visibility
|
|
120
|
+
this.clearIdleTimer();
|
|
121
|
+
this.idleTimer = setTimeout(() => {
|
|
122
|
+
this.idleTimer = null;
|
|
123
|
+
if (!this.pinned)
|
|
124
|
+
this.forceHide();
|
|
125
|
+
}, idleMs);
|
|
126
|
+
this.idleTimer.unref?.();
|
|
127
|
+
}
|
|
128
|
+
exitHookInstalled = false;
|
|
129
|
+
show() {
|
|
130
|
+
if (!this.enabled)
|
|
131
|
+
return;
|
|
132
|
+
this.installExitHook();
|
|
133
|
+
this.spawnPill();
|
|
134
|
+
this.spawnGlow();
|
|
135
|
+
}
|
|
136
|
+
/** The "control in progress" pill — also the BANNER_STOP / kill-switch source. */
|
|
137
|
+
spawnPill() {
|
|
138
|
+
if (this.child)
|
|
139
|
+
return;
|
|
140
|
+
let child;
|
|
141
|
+
try {
|
|
142
|
+
child = this.spawner();
|
|
143
|
+
}
|
|
144
|
+
catch {
|
|
145
|
+
return; // cosmetic feature — never let it break control
|
|
146
|
+
}
|
|
147
|
+
this.child = child;
|
|
148
|
+
if (child.stdout) {
|
|
149
|
+
const rl = readline.createInterface({ input: child.stdout });
|
|
150
|
+
rl.on('line', line => {
|
|
151
|
+
if (line.trim() === 'BANNER_STOP') {
|
|
152
|
+
try {
|
|
153
|
+
this.onStopRequested?.();
|
|
154
|
+
}
|
|
155
|
+
catch { /* never throw into the rl loop */ }
|
|
156
|
+
}
|
|
157
|
+
});
|
|
158
|
+
}
|
|
159
|
+
child.on('error', () => { if (this.child === child)
|
|
160
|
+
this.child = null; });
|
|
161
|
+
// If the pill dies, take the glow down with it so the two never desync
|
|
162
|
+
// into a half-on state (glow without the stop affordance).
|
|
163
|
+
child.on('exit', () => { if (this.child === child) {
|
|
164
|
+
this.child = null;
|
|
165
|
+
this.killGlow();
|
|
166
|
+
} });
|
|
167
|
+
}
|
|
168
|
+
/** The ambient screen-edge glow — pure visual, click-through, no stdout. */
|
|
169
|
+
spawnGlow() {
|
|
170
|
+
if (!this.glowEnabled || this.glowChild)
|
|
171
|
+
return;
|
|
172
|
+
let child;
|
|
173
|
+
try {
|
|
174
|
+
child = this.glowSpawner();
|
|
175
|
+
}
|
|
176
|
+
catch {
|
|
177
|
+
return; // cosmetic — never let it break control
|
|
178
|
+
}
|
|
179
|
+
this.glowChild = child;
|
|
180
|
+
child.on('error', () => { if (this.glowChild === child)
|
|
181
|
+
this.glowChild = null; });
|
|
182
|
+
child.on('exit', () => { if (this.glowChild === child)
|
|
183
|
+
this.glowChild = null; });
|
|
184
|
+
}
|
|
185
|
+
// Neither overlay may outlive its owner — an orphaned "control in progress"
|
|
186
|
+
// pill or glow after the daemon stopped would be a lie on screen. kill() is a
|
|
187
|
+
// sync syscall, safe inside an 'exit' handler.
|
|
188
|
+
installExitHook() {
|
|
189
|
+
if (this.exitHookInstalled)
|
|
190
|
+
return;
|
|
191
|
+
this.exitHookInstalled = true;
|
|
192
|
+
process.on('exit', () => {
|
|
193
|
+
try {
|
|
194
|
+
this.child?.kill();
|
|
195
|
+
}
|
|
196
|
+
catch { /* gone */ }
|
|
197
|
+
try {
|
|
198
|
+
this.glowChild?.kill();
|
|
199
|
+
}
|
|
200
|
+
catch { /* gone */ }
|
|
201
|
+
});
|
|
202
|
+
}
|
|
203
|
+
killGlow() {
|
|
204
|
+
const g = this.glowChild;
|
|
205
|
+
this.glowChild = null;
|
|
206
|
+
if (g) {
|
|
207
|
+
try {
|
|
208
|
+
g.kill();
|
|
209
|
+
}
|
|
210
|
+
catch { /* already gone */ }
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
forceHide() {
|
|
214
|
+
this.clearIdleTimer();
|
|
215
|
+
const c = this.child;
|
|
216
|
+
this.child = null;
|
|
217
|
+
if (c) {
|
|
218
|
+
try {
|
|
219
|
+
c.kill();
|
|
220
|
+
}
|
|
221
|
+
catch { /* already gone */ }
|
|
222
|
+
}
|
|
223
|
+
this.killGlow();
|
|
224
|
+
}
|
|
225
|
+
clearIdleTimer() {
|
|
226
|
+
if (this.idleTimer) {
|
|
227
|
+
clearTimeout(this.idleTimer);
|
|
228
|
+
this.idleTimer = null;
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
// ── test hooks ──
|
|
232
|
+
__setSpawnerForTests(s) { this.spawner = s; }
|
|
233
|
+
__setGlowSpawnerForTests(s) { this.glowSpawner = s; }
|
|
234
|
+
__setPlatformEnabledForTests(v) { this.enabled = v; }
|
|
235
|
+
__setGlowEnabledForTests(v) { this.glowEnabled = v; }
|
|
236
|
+
__resetForTests() {
|
|
237
|
+
this.forceHide();
|
|
238
|
+
this.pinned = false;
|
|
239
|
+
this.glowEnabled = true;
|
|
240
|
+
this.onStopRequested = undefined;
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
/** Singleton — one banner per process (daemon or stdio MCP server). */
|
|
244
|
+
exports.controlBanner = new ControlBanner();
|
|
245
|
+
//# sourceMappingURL=banner.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"banner.js","sourceRoot":"","sources":["../../src/core/banner.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAEH,iDAAyD;AACzD,2CAA6B;AAC7B,mDAAqC;AACrC,oCAA0C;AAE1C,MAAM,YAAY,GAAG,MAAM,CAAC;AAI5B,MAAM,aAAa;IACT,KAAK,GAAwB,IAAI,CAAC,CAAQ,iCAAiC;IAC3E,SAAS,GAAwB,IAAI,CAAC,CAAI,+BAA+B;IACzE,SAAS,GAAyC,IAAI,CAAC;IACvD,MAAM,GAAG,KAAK,CAAC;IACf,OAAO,GAAG,OAAO,CAAC,QAAQ,KAAK,OAAO,IAAI,OAAO,CAAC,GAAG,CAAC,eAAe,KAAK,GAAG,CAAC;IACtF,0EAA0E;IAC1E,4EAA4E;IAC5E,8CAA8C;IACtC,WAAW,GAAG,OAAO,CAAC,GAAG,CAAC,aAAa,KAAK,GAAG,CAAC;IAChD,eAAe,CAA2B;IAC1C,OAAO,GAAY,GAAG,EAAE;QAC9B,MAAM,MAAM,GAAG,IAAI,CAAC,IAAI,CAAC,IAAA,sBAAc,GAAE,EAAE,SAAS,EAAE,YAAY,CAAC,CAAC;QACpE,OAAO,IAAA,qBAAK,EAAC,gBAAgB,EAC3B,CAAC,YAAY,EAAE,kBAAkB,EAAE,QAAQ,EAAE,cAAc,EAAE,QAAQ,EAAE,OAAO,EAAE,MAAM,CAAC,EACvF,EAAE,KAAK,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,CAAC,CAAC;IAC7C,CAAC,CAAC;IACM,WAAW,GAAY,GAAG,EAAE;QAClC,MAAM,MAAM,GAAG,IAAI,CAAC,IAAI,CAAC,IAAA,sBAAc,GAAE,EAAE,SAAS,EAAE,eAAe,CAAC,CAAC;QACvE,OAAO,IAAA,qBAAK,EAAC,gBAAgB,EAC3B,CAAC,YAAY,EAAE,kBAAkB,EAAE,QAAQ,EAAE,cAAc,EAAE,QAAQ,EAAE,OAAO,EAAE,MAAM,CAAC,EACvF,EAAE,KAAK,EAAE,CAAC,QAAQ,EAAE,QAAQ,EAAE,QAAQ,CAAC,EAAE,CAAC,CAAC,CAAC,iDAAiD;IACjG,CAAC,CAAC;IAEF,2EAA2E;IAC3E,SAAS,CAAC,IAAsC;QAC9C,IAAI,CAAC,eAAe,GAAG,IAAI,CAAC,eAAe,CAAC;IAC9C,CAAC;IAED,mFAAmF;IACnF,UAAU,CAAC,CAAU;QACnB,IAAI,CAAC,OAAO,GAAG,CAAC,IAAI,OAAO,CAAC,QAAQ,KAAK,OAAO,IAAI,OAAO,CAAC,GAAG,CAAC,eAAe,KAAK,GAAG,CAAC;QACxF,IAAI,CAAC,IAAI,CAAC,OAAO;YAAE,IAAI,CAAC,SAAS,EAAE,CAAC;IACtC,CAAC;IAED,SAAS;QACP,OAAO,IAAI,CAAC,KAAK,KAAK,IAAI,CAAC;IAC7B,CAAC;IAED,yEAAyE;IACzE,GAAG;QACD,IAAI,CAAC,IAAI,CAAC,OAAO;YAAE,OAAO;QAC1B,IAAI,CAAC,MAAM,GAAG,IAAI,CAAC;QACnB,IAAI,CAAC,cAAc,EAAE,CAAC;QACtB,IAAI,CAAC,IAAI,EAAE,CAAC;IACd,CAAC;IAED,sEAAsE;IACtE,KAAK;QACH,IAAI,CAAC,MAAM,GAAG,KAAK,CAAC;QACpB,IAAI,CAAC,SAAS,EAAE,CAAC;IACnB,CAAC;IAED;;;OAGG;IACH,KAAK,CAAC,SAAiB,YAAY;QACjC,IAAI,CAAC,IAAI,CAAC,OAAO;YAAE,OAAO;QAC1B,IAAI,CAAC,IAAI,EAAE,CAAC;QACZ,IAAI,IAAI,CAAC,MAAM;YAAE,OAAO,CAAC,iCAAiC;QAC1D,IAAI,CAAC,cAAc,EAAE,CAAC;QACtB,IAAI,CAAC,SAAS,GAAG,UAAU,CAAC,GAAG,EAAE;YAC/B,IAAI,CAAC,SAAS,GAAG,IAAI,CAAC;YACtB,IAAI,CAAC,IAAI,CAAC,MAAM;gBAAE,IAAI,CAAC,SAAS,EAAE,CAAC;QACrC,CAAC,EAAE,MAAM,CAAC,CAAC;QACX,IAAI,CAAC,SAAS,CAAC,KAAK,EAAE,EAAE,CAAC;IAC3B,CAAC;IAEO,iBAAiB,GAAG,KAAK,CAAC;IAE1B,IAAI;QACV,IAAI,CAAC,IAAI,CAAC,OAAO;YAAE,OAAO;QAC1B,IAAI,CAAC,eAAe,EAAE,CAAC;QACvB,IAAI,CAAC,SAAS,EAAE,CAAC;QACjB,IAAI,CAAC,SAAS,EAAE,CAAC;IACnB,CAAC;IAED,kFAAkF;IAC1E,SAAS;QACf,IAAI,IAAI,CAAC,KAAK;YAAE,OAAO;QACvB,IAAI,KAAmB,CAAC;QACxB,IAAI,CAAC;YACH,KAAK,GAAG,IAAI,CAAC,OAAO,EAAE,CAAC;QACzB,CAAC;QAAC,MAAM,CAAC;YACP,OAAO,CAAC,gDAAgD;QAC1D,CAAC;QACD,IAAI,CAAC,KAAK,GAAG,KAAK,CAAC;QACnB,IAAI,KAAK,CAAC,MAAM,EAAE,CAAC;YACjB,MAAM,EAAE,GAAG,QAAQ,CAAC,eAAe,CAAC,EAAE,KAAK,EAAE,KAAK,CAAC,MAAM,EAAE,CAAC,CAAC;YAC7D,EAAE,CAAC,EAAE,CAAC,MAAM,EAAE,IAAI,CAAC,EAAE;gBACnB,IAAI,IAAI,CAAC,IAAI,EAAE,KAAK,aAAa,EAAE,CAAC;oBAClC,IAAI,CAAC;wBAAC,IAAI,CAAC,eAAe,EAAE,EAAE,CAAC;oBAAC,CAAC;oBAAC,MAAM,CAAC,CAAC,kCAAkC,CAAC,CAAC;gBAChF,CAAC;YACH,CAAC,CAAC,CAAC;QACL,CAAC;QACD,KAAK,CAAC,EAAE,CAAC,OAAO,EAAE,GAAG,EAAE,GAAG,IAAI,IAAI,CAAC,KAAK,KAAK,KAAK;YAAE,IAAI,CAAC,KAAK,GAAG,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC;QAC1E,uEAAuE;QACvE,2DAA2D;QAC3D,KAAK,CAAC,EAAE,CAAC,MAAM,EAAE,GAAG,EAAE,GAAG,IAAI,IAAI,CAAC,KAAK,KAAK,KAAK,EAAE,CAAC;YAAC,IAAI,CAAC,KAAK,GAAG,IAAI,CAAC;YAAC,IAAI,CAAC,QAAQ,EAAE,CAAC;QAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IAChG,CAAC;IAED,4EAA4E;IACpE,SAAS;QACf,IAAI,CAAC,IAAI,CAAC,WAAW,IAAI,IAAI,CAAC,SAAS;YAAE,OAAO;QAChD,IAAI,KAAmB,CAAC;QACxB,IAAI,CAAC;YACH,KAAK,GAAG,IAAI,CAAC,WAAW,EAAE,CAAC;QAC7B,CAAC;QAAC,MAAM,CAAC;YACP,OAAO,CAAC,wCAAwC;QAClD,CAAC;QACD,IAAI,CAAC,SAAS,GAAG,KAAK,CAAC;QACvB,KAAK,CAAC,EAAE,CAAC,OAAO,EAAE,GAAG,EAAE,GAAG,IAAI,IAAI,CAAC,SAAS,KAAK,KAAK;YAAE,IAAI,CAAC,SAAS,GAAG,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC;QAClF,KAAK,CAAC,EAAE,CAAC,MAAM,EAAE,GAAG,EAAE,GAAG,IAAI,IAAI,CAAC,SAAS,KAAK,KAAK;YAAE,IAAI,CAAC,SAAS,GAAG,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC;IACnF,CAAC;IAED,4EAA4E;IAC5E,8EAA8E;IAC9E,+CAA+C;IACvC,eAAe;QACrB,IAAI,IAAI,CAAC,iBAAiB;YAAE,OAAO;QACnC,IAAI,CAAC,iBAAiB,GAAG,IAAI,CAAC;QAC9B,OAAO,CAAC,EAAE,CAAC,MAAM,EAAE,GAAG,EAAE;YACtB,IAAI,CAAC;gBAAC,IAAI,CAAC,KAAK,EAAE,IAAI,EAAE,CAAC;YAAC,CAAC;YAAC,MAAM,CAAC,CAAC,UAAU,CAAC,CAAC;YAChD,IAAI,CAAC;gBAAC,IAAI,CAAC,SAAS,EAAE,IAAI,EAAE,CAAC;YAAC,CAAC;YAAC,MAAM,CAAC,CAAC,UAAU,CAAC,CAAC;QACtD,CAAC,CAAC,CAAC;IACL,CAAC;IAEO,QAAQ;QACd,MAAM,CAAC,GAAG,IAAI,CAAC,SAAS,CAAC;QACzB,IAAI,CAAC,SAAS,GAAG,IAAI,CAAC;QACtB,IAAI,CAAC,EAAE,CAAC;YACN,IAAI,CAAC;gBAAC,CAAC,CAAC,IAAI,EAAE,CAAC;YAAC,CAAC;YAAC,MAAM,CAAC,CAAC,kBAAkB,CAAC,CAAC;QAChD,CAAC;IACH,CAAC;IAEO,SAAS;QACf,IAAI,CAAC,cAAc,EAAE,CAAC;QACtB,MAAM,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC;QACrB,IAAI,CAAC,KAAK,GAAG,IAAI,CAAC;QAClB,IAAI,CAAC,EAAE,CAAC;YACN,IAAI,CAAC;gBAAC,CAAC,CAAC,IAAI,EAAE,CAAC;YAAC,CAAC;YAAC,MAAM,CAAC,CAAC,kBAAkB,CAAC,CAAC;QAChD,CAAC;QACD,IAAI,CAAC,QAAQ,EAAE,CAAC;IAClB,CAAC;IAEO,cAAc;QACpB,IAAI,IAAI,CAAC,SAAS,EAAE,CAAC;YACnB,YAAY,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;YAC7B,IAAI,CAAC,SAAS,GAAG,IAAI,CAAC;QACxB,CAAC;IACH,CAAC;IAED,mBAAmB;IACnB,oBAAoB,CAAC,CAAU,IAAU,IAAI,CAAC,OAAO,GAAG,CAAC,CAAC,CAAC,CAAC;IAC5D,wBAAwB,CAAC,CAAU,IAAU,IAAI,CAAC,WAAW,GAAG,CAAC,CAAC,CAAC,CAAC;IACpE,4BAA4B,CAAC,CAAU,IAAU,IAAI,CAAC,OAAO,GAAG,CAAC,CAAC,CAAC,CAAC;IACpE,wBAAwB,CAAC,CAAU,IAAU,IAAI,CAAC,WAAW,GAAG,CAAC,CAAC,CAAC,CAAC;IACpE,eAAe;QACb,IAAI,CAAC,SAAS,EAAE,CAAC;QACjB,IAAI,CAAC,MAAM,GAAG,KAAK,CAAC;QACpB,IAAI,CAAC,WAAW,GAAG,IAAI,CAAC;QACxB,IAAI,CAAC,eAAe,GAAG,SAAS,CAAC;IACnC,CAAC;CACF;AAED,uEAAuE;AAC1D,QAAA,aAAa,GAAG,IAAI,aAAa,EAAE,CAAC"}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Capability classifier — finer-grained tagging of subtask INTENT.
|
|
3
|
+
*
|
|
4
|
+
* Lives alongside `classifyTask` but asks a different question.
|
|
5
|
+
* `classifyTask` buckets into {mechanical, navigation, reasoning, spatial}
|
|
6
|
+
* to pick a PIPELINE PATH (router / blind / hybrid / vision). The
|
|
7
|
+
* capability classifier is the layer below: given this subtask's text,
|
|
8
|
+
* which TOOLS does the LLM plausibly need?
|
|
9
|
+
*
|
|
10
|
+
* Pure regex, zero LLM, ~30 LOC. Falls back to `general` (full tool
|
|
11
|
+
* catalog, same as pre-Tranche-2.5 behavior) when nothing matches, so
|
|
12
|
+
* this change is safe by construction.
|
|
13
|
+
*
|
|
14
|
+
* Precedence (first match wins):
|
|
15
|
+
* spatial > window_mgmt > file_ops > form_fill > text_input >
|
|
16
|
+
* navigation > app_launch > general
|
|
17
|
+
*
|
|
18
|
+
* Why that order: the most specific verbs fire first. "drag file to
|
|
19
|
+
* trash" matches both `spatial` (drag) and `file_ops` (file); spatial
|
|
20
|
+
* wins because the user's INTENT is a physical gesture, and the
|
|
21
|
+
* correct tool palette is the spatial one (needs drag/mouse tools,
|
|
22
|
+
* not file APIs).
|
|
23
|
+
*/
|
|
24
|
+
export type Capability =
|
|
25
|
+
/** `open Notepad`, `launch Chrome`, `focus Outlook`. Router usually handles — agent fallback needs launch + focus tools only. */
|
|
26
|
+
'app_launch'
|
|
27
|
+
/** `type hello`, `enter your email`, `write "foo"`. Focus + type + a11y set-value. */
|
|
28
|
+
| 'text_input'
|
|
29
|
+
/** `go to github.com`, `visit docs.anthropic.com`. Router handles — agent fallback wants key_press + switch_tab. */
|
|
30
|
+
| 'navigation'
|
|
31
|
+
/** `fill out the form`, `complete the signup`. Form iteration over named fields. */
|
|
32
|
+
| 'form_fill'
|
|
33
|
+
/** `draw a square`, `drag the file to trash`, `resize this element`. Needs pixel-level mouse primitives. */
|
|
34
|
+
| 'spatial'
|
|
35
|
+
/** `open the README`, `save as`, `open this URL`. System-open helpers + clipboard. */
|
|
36
|
+
| 'file_ops'
|
|
37
|
+
/** `maximize`, `minimize`, `close window`, `resize to 1280x720`. setWindowState + setWindowBounds. */
|
|
38
|
+
| 'window_mgmt'
|
|
39
|
+
/** Fallback — shows the full catalog. Same behavior as before this feature existed. */
|
|
40
|
+
| 'general';
|
|
41
|
+
/**
|
|
42
|
+
* Classify a subtask string into a Capability. Pure, zero-LLM.
|
|
43
|
+
* Caller uses the result to scope the agent's tool catalog.
|
|
44
|
+
*/
|
|
45
|
+
export declare function classifyCapability(subtaskText: string): Capability;
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Capability classifier — finer-grained tagging of subtask INTENT.
|
|
4
|
+
*
|
|
5
|
+
* Lives alongside `classifyTask` but asks a different question.
|
|
6
|
+
* `classifyTask` buckets into {mechanical, navigation, reasoning, spatial}
|
|
7
|
+
* to pick a PIPELINE PATH (router / blind / hybrid / vision). The
|
|
8
|
+
* capability classifier is the layer below: given this subtask's text,
|
|
9
|
+
* which TOOLS does the LLM plausibly need?
|
|
10
|
+
*
|
|
11
|
+
* Pure regex, zero LLM, ~30 LOC. Falls back to `general` (full tool
|
|
12
|
+
* catalog, same as pre-Tranche-2.5 behavior) when nothing matches, so
|
|
13
|
+
* this change is safe by construction.
|
|
14
|
+
*
|
|
15
|
+
* Precedence (first match wins):
|
|
16
|
+
* spatial > window_mgmt > file_ops > form_fill > text_input >
|
|
17
|
+
* navigation > app_launch > general
|
|
18
|
+
*
|
|
19
|
+
* Why that order: the most specific verbs fire first. "drag file to
|
|
20
|
+
* trash" matches both `spatial` (drag) and `file_ops` (file); spatial
|
|
21
|
+
* wins because the user's INTENT is a physical gesture, and the
|
|
22
|
+
* correct tool palette is the spatial one (needs drag/mouse tools,
|
|
23
|
+
* not file APIs).
|
|
24
|
+
*/
|
|
25
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
26
|
+
exports.classifyCapability = classifyCapability;
|
|
27
|
+
const CAPABILITY_RULES = [
|
|
28
|
+
// spatial — physical gestures. Leading check so "drag file to trash" lands here.
|
|
29
|
+
{
|
|
30
|
+
name: 'spatial',
|
|
31
|
+
pattern: /\b(draw|sketch|paint|drag\s+.*\b(to|onto|into)\b|resize\s+(the\s+)?(element|image|layer)|crop|rotate|annotate|illustrate|diagram)\b/i,
|
|
32
|
+
},
|
|
33
|
+
// window_mgmt — verbs about the window itself
|
|
34
|
+
{
|
|
35
|
+
name: 'window_mgmt',
|
|
36
|
+
pattern: /\b(maximi[sz]e|minimi[sz]e|close\s+(the\s+)?(window|tab)|resize\s+(the\s+)?window|move\s+(the\s+)?window\s+to|restore\s+(the\s+)?window|snap\s+(to\s+)?(left|right|top|bottom))\b/i,
|
|
37
|
+
},
|
|
38
|
+
// file_ops — file system / URL targets
|
|
39
|
+
{
|
|
40
|
+
name: 'file_ops',
|
|
41
|
+
pattern: /\b(open\s+(the\s+)?file|save\s+as|open\s+in\s+default|show\s+in\s+(finder|explorer)|open\s+(this\s+)?url|open\s+https?:|copy\s+(file|text|url)\s+to\s+clipboard)\b/i,
|
|
42
|
+
},
|
|
43
|
+
// form_fill — explicit form interaction
|
|
44
|
+
{
|
|
45
|
+
name: 'form_fill',
|
|
46
|
+
pattern: /\b(fill\s+(in|out|the)|complete\s+(the\s+)?form|check\s+(the\s+)?box|select\s+(the\s+)?(option|item|radio)|submit\s+(the\s+)?form)\b/i,
|
|
47
|
+
},
|
|
48
|
+
// text_input — anything saying type / enter / write text
|
|
49
|
+
{
|
|
50
|
+
name: 'text_input',
|
|
51
|
+
pattern: /\b(type\b|enter\s+["'\w]|write\s+["'\w]|paste\s+["'\w]|input\s+["'\w]|key\s*in\b)/i,
|
|
52
|
+
},
|
|
53
|
+
// navigation — URL or "go to" style
|
|
54
|
+
{
|
|
55
|
+
name: 'navigation',
|
|
56
|
+
pattern: /\b(go\s+to|navigate\s+to|visit|browse\s+to|search\s+(for|on\s+page)|find\s+on\s+page|switch\s+to\s+.*tab)\b/i,
|
|
57
|
+
},
|
|
58
|
+
// app_launch — open/launch/focus app
|
|
59
|
+
{
|
|
60
|
+
name: 'app_launch',
|
|
61
|
+
pattern: /^\s*(open|launch|start|run|focus|switch\s+to)\b/i,
|
|
62
|
+
},
|
|
63
|
+
];
|
|
64
|
+
/**
|
|
65
|
+
* Classify a subtask string into a Capability. Pure, zero-LLM.
|
|
66
|
+
* Caller uses the result to scope the agent's tool catalog.
|
|
67
|
+
*/
|
|
68
|
+
function classifyCapability(subtaskText) {
|
|
69
|
+
const t = subtaskText.trim();
|
|
70
|
+
if (!t)
|
|
71
|
+
return 'general';
|
|
72
|
+
for (const rule of CAPABILITY_RULES) {
|
|
73
|
+
if (rule.pattern.test(t))
|
|
74
|
+
return rule.name;
|
|
75
|
+
}
|
|
76
|
+
return 'general';
|
|
77
|
+
}
|
|
78
|
+
//# sourceMappingURL=capability.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"capability.js","sourceRoot":"","sources":["../../../src/core/classify/capability.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;;AAoEH,gDAOC;AAvDD,MAAM,gBAAgB,GAAiD;IACrE,iFAAiF;IACjF;QACE,IAAI,EAAE,SAAS;QACf,OAAO,EAAE,sIAAsI;KAChJ;IAED,8CAA8C;IAC9C;QACE,IAAI,EAAE,aAAa;QACnB,OAAO,EAAE,oLAAoL;KAC9L;IAED,uCAAuC;IACvC;QACE,IAAI,EAAE,UAAU;QAChB,OAAO,EAAE,qKAAqK;KAC/K;IAED,wCAAwC;IACxC;QACE,IAAI,EAAE,WAAW;QACjB,OAAO,EAAE,uIAAuI;KACjJ;IAED,yDAAyD;IACzD;QACE,IAAI,EAAE,YAAY;QAClB,OAAO,EAAE,oFAAoF;KAC9F;IAED,oCAAoC;IACpC;QACE,IAAI,EAAE,YAAY;QAClB,OAAO,EAAE,8GAA8G;KACxH;IAED,qCAAqC;IACrC;QACE,IAAI,EAAE,YAAY;QAClB,OAAO,EAAE,kDAAkD;KAC5D;CACF,CAAC;AAEF;;;GAGG;AACH,SAAgB,kBAAkB,CAAC,WAAmB;IACpD,MAAM,CAAC,GAAG,WAAW,CAAC,IAAI,EAAE,CAAC;IAC7B,IAAI,CAAC,CAAC;QAAE,OAAO,SAAS,CAAC;IACzB,KAAK,MAAM,IAAI,IAAI,gBAAgB,EAAE,CAAC;QACpC,IAAI,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC;YAAE,OAAO,IAAI,CAAC,IAAI,CAAC;IAC7C,CAAC;IACD,OAAO,SAAS,CAAC;AACnB,CAAC"}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* LLM task decomposer — fallback when the offline parser returns null.
|
|
3
|
+
*
|
|
4
|
+
* Ported from src/ai-brain.ts::decomposeTask + DECOMPOSE_SYSTEM_PROMPT.
|
|
5
|
+
* Pure text LLM call, no screenshots, no tool calls. One input/output round.
|
|
6
|
+
*
|
|
7
|
+
* v0.8.1 scaffold: exposes a clean interface the pipeline can wire. The
|
|
8
|
+
* actual LLM call is injected — the text-agent's LLM client and this
|
|
9
|
+
* module's client share infrastructure. Keeps this file pure/testable.
|
|
10
|
+
*/
|
|
11
|
+
export declare const DECOMPOSE_SYSTEM_PROMPT = "You are a task decomposer. Read the ENTIRE input first, then break the natural-language desktop task into an ordered list of concrete, atomic subtasks.\n\nRules:\n- Use ONE concrete action per subtask string.\n- \"type\" subtasks MUST contain the literal text to type, never an instruction.\n Bad: \"Type the user's name\"\n Good: \"type John Smith\"\n- Subtasks must be in execution order.\n- USE AS FEW SUBTASKS AS POSSIBLE. Hard cap is 8. Each subtask runs through\n its own perception+plan+act loop IN COMPLETE ISOLATION \u2014 the agent for\n subtask 3 has NO IDEA what subtask 2 did. State does NOT persist between\n subtasks. So chunky \"do the whole thing\" subtasks cost less and succeed\n more than micro-steps.\n- SINGLE-APP WORKFLOWS ARE ONE SUBTASK. If the entire task happens in one\n app, emit just \"open <app>\" + \"do the thing in <app>\" \u2014 at most 2\n subtasks. Do NOT break click/type/click sequences inside one app into\n separate subtasks. This rule is app-agnostic; it applies to any app.\n Bad: [\"open <mail app>\", \"click Compose\", \"type address\", \"type subject\", \"type body\", \"click Send\"]\n Why: 6 isolated agents, each blind to what prior agents did. Subtask 4\n runs, sees the app but no compose window (subtask 2's window\n closed), fails.\n Good: [\"open <mail app>\", \"compose and send an email to john@example.com introducing yourself\"]\n Why: 1 agent opens the app, 1 agent does the entire compose flow.\n Same rule for any app: spreadsheet edits, image edits, document writing,\n music playback, calendar events, chat messages \u2014 always one subtask for\n \"open the app\", one subtask for the WHOLE in-app workflow.\n- Each subtask must be SELF-CONTAINED. The downstream agent sees ONLY this\n one string, not the original task or prior subtasks. So every subtask must\n carry enough context to execute correctly on its own.\n Bad task split: [\"open Edge\", \"navigate to outlook.office.com\", \"wait for Outlook to load\", \"click compose\"]\n Why bad: subtask 3 \"wait for Outlook to load\" \u2014 the agent has no idea this means the WEB page in Edge.\n It will see no desktop Outlook running and incorrectly call open_app(\"Outlook\").\n Good split: [\"navigate to https://outlook.office.com in the default browser\", \"click the New / Compose button on outlook.office.com\"]\n Why good: each step names what app/page it's acting on, no orphan \"wait\" steps, no scaffolding.\n- DROP \"wait for X to load\" subtasks entirely. The downstream agent's\n perception loop already polls the screen \u2014 explicit waits are scaffolding\n the OS handles for free, and they confuse the agent in isolation.\n- DROP \"create a new canvas / new document / new sheet / new tab / new file\"\n subtasks when they immediately follow an \"open <app>\" step. Apps open\n with a fresh blank state by default \u2014 Paint opens with a blank canvas,\n Word opens with a blank document, browsers open with a new tab. Adding\n a \"create new X\" subtask after \"open X\" creates a phantom no-op step\n that the downstream verifier flags as failure (zero pixel change \u2192 false\n negative), which can kill the chain before the actual work runs.\n Bad: [\"open Paint\", \"create a new canvas in Paint\", \"draw a stickfigure\"]\n Good: [\"open Paint\", \"draw a stickfigure\"]\n Bad: [\"open Notepad\", \"create a new document\", \"type Hello\"]\n Good: [\"open Notepad\", \"type Hello\"]\n Only emit a \"create new X\" step when the user EXPLICITLY says they're\n working in an already-open instance (e.g. \"in my open Word doc, start a\n new section\" \u2014 that's a real new-section step, not scaffolding).\n- NEVER bundle an app LAUNCH with an in-app ACTION in one subtask string.\n \"open X\" must be its OWN subtask, separate from what you do inside X. A\n bundled \"open X and <do Y>\" is both un-routable (looks compound) and gets\n mis-handled (the launch never happens cleanly).\n Bad: [\"open Notepad and paste the copied text\"]\n Good: [\"open Notepad\", \"paste the copied text\"]\n Bad: [\"open Calculator and compute 5*7\"]\n Good: [\"open Calculator\", \"compute 5*7\"]\n- NAME THE WINDOW in a dependent subtask. When subtask N opens an app or\n navigates to a page and subtask N+1 acts INSIDE it, the N+1 string MUST name\n that app/page \u2014 each subtask runs in isolation and otherwise opens the wrong\n app. (e.g. after \"navigate to docs.google.com\", the next subtask must say \"in\n the Google Docs tab\" or it will open Notepad and type there.)\n Bad: [\"navigate to docs.google.com\", \"create a document and type a sentence\"]\n Good: [\"navigate to docs.google.com\", \"in the Google Docs tab, start a new document and type a sentence about dogs\"]\n- Verbs to prefer: open, focus, click, type, press, navigate, select, scroll, save, send.\n Avoid: wait (redundant \u2014 see above), check, verify (the verifier handles those).\n- Do NOT invent information the task didn't provide. If an email address or value is missing, leave the subtask at the level of \"type the recipient email\".\n\nINTERPRET INDEFINITE PHRASES (CRITICAL \u2014 agents below this layer take strings literally):\n- \"any X\" / \"a random X\" / \"some X\" means \"pick a representative X yourself\" \u2014 RESOLVE it into a concrete step. Do NOT pass the indefinite phrase through.\n Bad: \"open any Wikipedia page\" \u2192 Good: \"navigate to https://en.wikipedia.org/wiki/Special:Random\"\n Bad: \"search for any restaurant\" \u2192 Good: \"navigate to https://www.google.com/maps/search/restaurants near me\"\n Bad: \"open a random YouTube video\" \u2192 Good: \"navigate to https://www.youtube.com\" then \"click the first recommended video\"\n- \"the latest X\" / \"today's X\" means \"the most recent visible X\" \u2014 pick the topmost / first-listed item in the relevant view. Don't ask the user.\n Bad: \"summarize the latest email\" \u2192 Good: \"open Outlook\" then \"click the first email in the inbox\"\n- \"an example\" / \"anything\" \u2014 same rule: pick something concrete and proceed.\n\nDO NOT EMIT SCAFFOLDING STEPS the OS does for free:\n- A \"navigate to <url>\" subtask ALREADY launches the default browser as a side effect \u2014 the OS opens whichever browser is the registered http handler. Do NOT emit a separate \"open default browser\" / \"open Chrome\" / \"open Edge\" step before it. That makes the agent type the literal phrase \"default browser\" into a search bar.\n Bad: [\"open default browser\", \"navigate to https://github.com\"]\n Good: [\"navigate to https://github.com\"]\n- Same applies to \"open default mail client\" / \"open the default editor\" \u2014 drop the scaffolding, the OS routes the right app.\n- If the user EXPLICITLY named a browser (\"open Chrome and go to github.com\"), you MAY emit \"open Chrome\" as a step \u2014 that's a concrete app name, not scaffolding.\n\nApp-name normalization (CRITICAL \u2014 wrong app names cause launch failures):\n- Use the canonical short name. Strip filler words: \"app\", \"application\",\n \"browser\", \"window\", \"program\", and articles (\"the\", \"a\", \"an\") when they\n precede a brand name.\n Bad: \"open the Outlook app\" \u2192 Good: \"open Outlook\"\n Bad: \"launch Edge browser\" \u2192 Good: \"open Edge\"\n Bad: \"start the calculator app\" \u2192 Good: \"open Calculator\"\n Bad: \"run Microsoft Word app\" \u2192 Good: \"open Microsoft Word\"\n- Keep brand-qualified names that the user gave you (\"Microsoft Word\",\n \"Google Chrome\") \u2014 those are the canonical alias keys, not filler.\n\nOutput FORMAT \u2014 JSON only, no prose:\n{ \"subtasks\": [\"...\", \"...\"] }";
|
|
12
|
+
export interface LlmDecomposerDeps {
|
|
13
|
+
/**
|
|
14
|
+
* Text-only LLM call. Returns the raw model output. Caller is responsible
|
|
15
|
+
* for retries and timeouts — this module just composes the prompt and parses.
|
|
16
|
+
*/
|
|
17
|
+
callTextLlm: (systemPrompt: string, userPrompt: string, opts?: {
|
|
18
|
+
maxTokens?: number;
|
|
19
|
+
}) => Promise<string>;
|
|
20
|
+
}
|
|
21
|
+
/** Extracts the first JSON object from a possibly-messy LLM response. */
|
|
22
|
+
export declare function extractJson(raw: string): unknown | null;
|
|
23
|
+
/**
|
|
24
|
+
* Decompose via LLM. Returns the subtasks array on success, null when the
|
|
25
|
+
* response can't be parsed or is empty (caller falls through to one-shot
|
|
26
|
+
* text-agent).
|
|
27
|
+
*
|
|
28
|
+
* `desktopContext` (optional) is a rendered DesktopSurvey block — the live
|
|
29
|
+
* list of open windows and resolved default handlers. When present it's
|
|
30
|
+
* prepended to the task so the model grounds decomposition in what's actually
|
|
31
|
+
* available (skip "open X" for an already-open app, route capabilities to the
|
|
32
|
+
* real default handler) instead of guessing. Passed as a plain string to keep
|
|
33
|
+
* this module decoupled from the survey type.
|
|
34
|
+
*/
|
|
35
|
+
export declare function decomposeWithLlm(task: string, deps: LlmDecomposerDeps, desktopContext?: string): Promise<string[] | null>;
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* LLM task decomposer — fallback when the offline parser returns null.
|
|
4
|
+
*
|
|
5
|
+
* Ported from src/ai-brain.ts::decomposeTask + DECOMPOSE_SYSTEM_PROMPT.
|
|
6
|
+
* Pure text LLM call, no screenshots, no tool calls. One input/output round.
|
|
7
|
+
*
|
|
8
|
+
* v0.8.1 scaffold: exposes a clean interface the pipeline can wire. The
|
|
9
|
+
* actual LLM call is injected — the text-agent's LLM client and this
|
|
10
|
+
* module's client share infrastructure. Keeps this file pure/testable.
|
|
11
|
+
*/
|
|
12
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
13
|
+
exports.DECOMPOSE_SYSTEM_PROMPT = void 0;
|
|
14
|
+
exports.extractJson = extractJson;
|
|
15
|
+
exports.decomposeWithLlm = decomposeWithLlm;
|
|
16
|
+
exports.DECOMPOSE_SYSTEM_PROMPT = `You are a task decomposer. Read the ENTIRE input first, then break the natural-language desktop task into an ordered list of concrete, atomic subtasks.
|
|
17
|
+
|
|
18
|
+
Rules:
|
|
19
|
+
- Use ONE concrete action per subtask string.
|
|
20
|
+
- "type" subtasks MUST contain the literal text to type, never an instruction.
|
|
21
|
+
Bad: "Type the user's name"
|
|
22
|
+
Good: "type John Smith"
|
|
23
|
+
- Subtasks must be in execution order.
|
|
24
|
+
- USE AS FEW SUBTASKS AS POSSIBLE. Hard cap is 8. Each subtask runs through
|
|
25
|
+
its own perception+plan+act loop IN COMPLETE ISOLATION — the agent for
|
|
26
|
+
subtask 3 has NO IDEA what subtask 2 did. State does NOT persist between
|
|
27
|
+
subtasks. So chunky "do the whole thing" subtasks cost less and succeed
|
|
28
|
+
more than micro-steps.
|
|
29
|
+
- SINGLE-APP WORKFLOWS ARE ONE SUBTASK. If the entire task happens in one
|
|
30
|
+
app, emit just "open <app>" + "do the thing in <app>" — at most 2
|
|
31
|
+
subtasks. Do NOT break click/type/click sequences inside one app into
|
|
32
|
+
separate subtasks. This rule is app-agnostic; it applies to any app.
|
|
33
|
+
Bad: ["open <mail app>", "click Compose", "type address", "type subject", "type body", "click Send"]
|
|
34
|
+
Why: 6 isolated agents, each blind to what prior agents did. Subtask 4
|
|
35
|
+
runs, sees the app but no compose window (subtask 2's window
|
|
36
|
+
closed), fails.
|
|
37
|
+
Good: ["open <mail app>", "compose and send an email to john@example.com introducing yourself"]
|
|
38
|
+
Why: 1 agent opens the app, 1 agent does the entire compose flow.
|
|
39
|
+
Same rule for any app: spreadsheet edits, image edits, document writing,
|
|
40
|
+
music playback, calendar events, chat messages — always one subtask for
|
|
41
|
+
"open the app", one subtask for the WHOLE in-app workflow.
|
|
42
|
+
- Each subtask must be SELF-CONTAINED. The downstream agent sees ONLY this
|
|
43
|
+
one string, not the original task or prior subtasks. So every subtask must
|
|
44
|
+
carry enough context to execute correctly on its own.
|
|
45
|
+
Bad task split: ["open Edge", "navigate to outlook.office.com", "wait for Outlook to load", "click compose"]
|
|
46
|
+
Why bad: subtask 3 "wait for Outlook to load" — the agent has no idea this means the WEB page in Edge.
|
|
47
|
+
It will see no desktop Outlook running and incorrectly call open_app("Outlook").
|
|
48
|
+
Good split: ["navigate to https://outlook.office.com in the default browser", "click the New / Compose button on outlook.office.com"]
|
|
49
|
+
Why good: each step names what app/page it's acting on, no orphan "wait" steps, no scaffolding.
|
|
50
|
+
- DROP "wait for X to load" subtasks entirely. The downstream agent's
|
|
51
|
+
perception loop already polls the screen — explicit waits are scaffolding
|
|
52
|
+
the OS handles for free, and they confuse the agent in isolation.
|
|
53
|
+
- DROP "create a new canvas / new document / new sheet / new tab / new file"
|
|
54
|
+
subtasks when they immediately follow an "open <app>" step. Apps open
|
|
55
|
+
with a fresh blank state by default — Paint opens with a blank canvas,
|
|
56
|
+
Word opens with a blank document, browsers open with a new tab. Adding
|
|
57
|
+
a "create new X" subtask after "open X" creates a phantom no-op step
|
|
58
|
+
that the downstream verifier flags as failure (zero pixel change → false
|
|
59
|
+
negative), which can kill the chain before the actual work runs.
|
|
60
|
+
Bad: ["open Paint", "create a new canvas in Paint", "draw a stickfigure"]
|
|
61
|
+
Good: ["open Paint", "draw a stickfigure"]
|
|
62
|
+
Bad: ["open Notepad", "create a new document", "type Hello"]
|
|
63
|
+
Good: ["open Notepad", "type Hello"]
|
|
64
|
+
Only emit a "create new X" step when the user EXPLICITLY says they're
|
|
65
|
+
working in an already-open instance (e.g. "in my open Word doc, start a
|
|
66
|
+
new section" — that's a real new-section step, not scaffolding).
|
|
67
|
+
- NEVER bundle an app LAUNCH with an in-app ACTION in one subtask string.
|
|
68
|
+
"open X" must be its OWN subtask, separate from what you do inside X. A
|
|
69
|
+
bundled "open X and <do Y>" is both un-routable (looks compound) and gets
|
|
70
|
+
mis-handled (the launch never happens cleanly).
|
|
71
|
+
Bad: ["open Notepad and paste the copied text"]
|
|
72
|
+
Good: ["open Notepad", "paste the copied text"]
|
|
73
|
+
Bad: ["open Calculator and compute 5*7"]
|
|
74
|
+
Good: ["open Calculator", "compute 5*7"]
|
|
75
|
+
- NAME THE WINDOW in a dependent subtask. When subtask N opens an app or
|
|
76
|
+
navigates to a page and subtask N+1 acts INSIDE it, the N+1 string MUST name
|
|
77
|
+
that app/page — each subtask runs in isolation and otherwise opens the wrong
|
|
78
|
+
app. (e.g. after "navigate to docs.google.com", the next subtask must say "in
|
|
79
|
+
the Google Docs tab" or it will open Notepad and type there.)
|
|
80
|
+
Bad: ["navigate to docs.google.com", "create a document and type a sentence"]
|
|
81
|
+
Good: ["navigate to docs.google.com", "in the Google Docs tab, start a new document and type a sentence about dogs"]
|
|
82
|
+
- Verbs to prefer: open, focus, click, type, press, navigate, select, scroll, save, send.
|
|
83
|
+
Avoid: wait (redundant — see above), check, verify (the verifier handles those).
|
|
84
|
+
- Do NOT invent information the task didn't provide. If an email address or value is missing, leave the subtask at the level of "type the recipient email".
|
|
85
|
+
|
|
86
|
+
INTERPRET INDEFINITE PHRASES (CRITICAL — agents below this layer take strings literally):
|
|
87
|
+
- "any X" / "a random X" / "some X" means "pick a representative X yourself" — RESOLVE it into a concrete step. Do NOT pass the indefinite phrase through.
|
|
88
|
+
Bad: "open any Wikipedia page" → Good: "navigate to https://en.wikipedia.org/wiki/Special:Random"
|
|
89
|
+
Bad: "search for any restaurant" → Good: "navigate to https://www.google.com/maps/search/restaurants near me"
|
|
90
|
+
Bad: "open a random YouTube video" → Good: "navigate to https://www.youtube.com" then "click the first recommended video"
|
|
91
|
+
- "the latest X" / "today's X" means "the most recent visible X" — pick the topmost / first-listed item in the relevant view. Don't ask the user.
|
|
92
|
+
Bad: "summarize the latest email" → Good: "open Outlook" then "click the first email in the inbox"
|
|
93
|
+
- "an example" / "anything" — same rule: pick something concrete and proceed.
|
|
94
|
+
|
|
95
|
+
DO NOT EMIT SCAFFOLDING STEPS the OS does for free:
|
|
96
|
+
- A "navigate to <url>" subtask ALREADY launches the default browser as a side effect — the OS opens whichever browser is the registered http handler. Do NOT emit a separate "open default browser" / "open Chrome" / "open Edge" step before it. That makes the agent type the literal phrase "default browser" into a search bar.
|
|
97
|
+
Bad: ["open default browser", "navigate to https://github.com"]
|
|
98
|
+
Good: ["navigate to https://github.com"]
|
|
99
|
+
- Same applies to "open default mail client" / "open the default editor" — drop the scaffolding, the OS routes the right app.
|
|
100
|
+
- If the user EXPLICITLY named a browser ("open Chrome and go to github.com"), you MAY emit "open Chrome" as a step — that's a concrete app name, not scaffolding.
|
|
101
|
+
|
|
102
|
+
App-name normalization (CRITICAL — wrong app names cause launch failures):
|
|
103
|
+
- Use the canonical short name. Strip filler words: "app", "application",
|
|
104
|
+
"browser", "window", "program", and articles ("the", "a", "an") when they
|
|
105
|
+
precede a brand name.
|
|
106
|
+
Bad: "open the Outlook app" → Good: "open Outlook"
|
|
107
|
+
Bad: "launch Edge browser" → Good: "open Edge"
|
|
108
|
+
Bad: "start the calculator app" → Good: "open Calculator"
|
|
109
|
+
Bad: "run Microsoft Word app" → Good: "open Microsoft Word"
|
|
110
|
+
- Keep brand-qualified names that the user gave you ("Microsoft Word",
|
|
111
|
+
"Google Chrome") — those are the canonical alias keys, not filler.
|
|
112
|
+
|
|
113
|
+
Output FORMAT — JSON only, no prose:
|
|
114
|
+
{ "subtasks": ["...", "..."] }`;
|
|
115
|
+
/** Extracts the first JSON object from a possibly-messy LLM response. */
|
|
116
|
+
function extractJson(raw) {
|
|
117
|
+
const fence = raw.match(/```(?:json)?\s*([\s\S]*?)```/);
|
|
118
|
+
const candidate = fence ? fence[1] : raw;
|
|
119
|
+
const start = candidate.indexOf('{');
|
|
120
|
+
const end = candidate.lastIndexOf('}');
|
|
121
|
+
if (start < 0 || end <= start)
|
|
122
|
+
return null;
|
|
123
|
+
try {
|
|
124
|
+
return JSON.parse(candidate.slice(start, end + 1));
|
|
125
|
+
}
|
|
126
|
+
catch {
|
|
127
|
+
return null;
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
/**
|
|
131
|
+
* Decompose via LLM. Returns the subtasks array on success, null when the
|
|
132
|
+
* response can't be parsed or is empty (caller falls through to one-shot
|
|
133
|
+
* text-agent).
|
|
134
|
+
*
|
|
135
|
+
* `desktopContext` (optional) is a rendered DesktopSurvey block — the live
|
|
136
|
+
* list of open windows and resolved default handlers. When present it's
|
|
137
|
+
* prepended to the task so the model grounds decomposition in what's actually
|
|
138
|
+
* available (skip "open X" for an already-open app, route capabilities to the
|
|
139
|
+
* real default handler) instead of guessing. Passed as a plain string to keep
|
|
140
|
+
* this module decoupled from the survey type.
|
|
141
|
+
*/
|
|
142
|
+
async function decomposeWithLlm(task, deps, desktopContext) {
|
|
143
|
+
const userPrompt = desktopContext && desktopContext.trim()
|
|
144
|
+
? `${desktopContext.trim()}\n\nTASK:\n${task}`
|
|
145
|
+
: task;
|
|
146
|
+
const raw = await deps.callTextLlm(exports.DECOMPOSE_SYSTEM_PROMPT, userPrompt, { maxTokens: 400 });
|
|
147
|
+
const parsed = extractJson(raw);
|
|
148
|
+
if (!parsed || typeof parsed !== 'object')
|
|
149
|
+
return null;
|
|
150
|
+
const subtasks = parsed.subtasks;
|
|
151
|
+
if (!Array.isArray(subtasks))
|
|
152
|
+
return null;
|
|
153
|
+
const filtered = subtasks.filter(s => typeof s === 'string' && s.trim().length > 0).map(s => s.trim());
|
|
154
|
+
return filtered.length > 0 ? filtered : null;
|
|
155
|
+
}
|
|
156
|
+
//# sourceMappingURL=llm-decomposer.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"llm-decomposer.js","sourceRoot":"","sources":["../../../src/core/decompose/llm-decomposer.ts"],"names":[],"mappings":";AAAA;;;;;;;;;GASG;;;AA+GH,kCAWC;AAcD,4CAeC;AArJY,QAAA,uBAAuB,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;+BAkGR,CAAC;AAUhC,yEAAyE;AACzE,SAAgB,WAAW,CAAC,GAAW;IACrC,MAAM,KAAK,GAAG,GAAG,CAAC,KAAK,CAAC,8BAA8B,CAAC,CAAC;IACxD,MAAM,SAAS,GAAG,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC;IACzC,MAAM,KAAK,GAAG,SAAS,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;IACrC,MAAM,GAAG,GAAG,SAAS,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC;IACvC,IAAI,KAAK,GAAG,CAAC,IAAI,GAAG,IAAI,KAAK;QAAE,OAAO,IAAI,CAAC;IAC3C,IAAI,CAAC;QACH,OAAO,IAAI,CAAC,KAAK,CAAC,SAAS,CAAC,KAAK,CAAC,KAAK,EAAE,GAAG,GAAG,CAAC,CAAC,CAAC,CAAC;IACrD,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC;AAED;;;;;;;;;;;GAWG;AACI,KAAK,UAAU,gBAAgB,CACpC,IAAY,EACZ,IAAuB,EACvB,cAAuB;IAEvB,MAAM,UAAU,GAAG,cAAc,IAAI,cAAc,CAAC,IAAI,EAAE;QACxD,CAAC,CAAC,GAAG,cAAc,CAAC,IAAI,EAAE,cAAc,IAAI,EAAE;QAC9C,CAAC,CAAC,IAAI,CAAC;IACT,MAAM,GAAG,GAAG,MAAM,IAAI,CAAC,WAAW,CAAC,+BAAuB,EAAE,UAAU,EAAE,EAAE,SAAS,EAAE,GAAG,EAAE,CAAC,CAAC;IAC5F,MAAM,MAAM,GAAG,WAAW,CAAC,GAAG,CAAC,CAAC;IAChC,IAAI,CAAC,MAAM,IAAI,OAAO,MAAM,KAAK,QAAQ;QAAE,OAAO,IAAI,CAAC;IACvD,MAAM,QAAQ,GAAI,MAAiC,CAAC,QAAQ,CAAC;IAC7D,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,QAAQ,CAAC;QAAE,OAAO,IAAI,CAAC;IAC1C,MAAM,QAAQ,GAAG,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,OAAO,CAAC,KAAK,QAAQ,IAAI,CAAC,CAAC,IAAI,EAAE,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAE,CAAY,CAAC,IAAI,EAAE,CAAC,CAAC;IACnH,OAAO,QAAQ,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC;AAC/C,CAAC"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Offline task decomposer — regex + quote-aware compound split.
|
|
3
|
+
*
|
|
4
|
+
* Ported from src/local-parser.ts. Zero LLM calls, zero dependencies. Returns
|
|
5
|
+
* the task split into subtask strings, or null when the task is too ambiguous
|
|
6
|
+
* to split safely (caller falls back to the LLM decomposer).
|
|
7
|
+
*
|
|
8
|
+
* v0.6.3 product knowledge preserved:
|
|
9
|
+
* - The `actionVerb` validator is the guard against dangerous splits like
|
|
10
|
+
* "scroll through and read" where one half lacks a terminal condition and
|
|
11
|
+
* would infinite-loop the text-agent.
|
|
12
|
+
* - Quote-aware splitter so "send 'hello, world' to Bob" isn't broken.
|
|
13
|
+
*/
|
|
14
|
+
export interface DecomposeResult {
|
|
15
|
+
subtasks: string[];
|
|
16
|
+
/** True when the parser chose to keep the task as one unit despite
|
|
17
|
+
* encountering delimiters — signals "we deliberately did not split". */
|
|
18
|
+
keptAsOne: boolean;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Split a compound task on ` and `, ` then `, or `,` — quote-aware.
|
|
22
|
+
*/
|
|
23
|
+
export declare function splitCompound(task: string): string[];
|
|
24
|
+
/**
|
|
25
|
+
* Decompose a task. Null → ambiguous, caller escalates to LLM decomposer.
|
|
26
|
+
*/
|
|
27
|
+
export declare function decompose(task: string): DecomposeResult | null;
|