@gajae-code/coding-agent 0.11.3 → 0.11.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 +47 -0
- package/dist/types/cli/notify-cli.d.ts +5 -1
- package/dist/types/cli/worktree-cli.d.ts +8 -25
- package/dist/types/commands/gc.d.ts +4 -0
- package/dist/types/commands/worktree.d.ts +7 -32
- package/dist/types/config/settings-schema.d.ts +20 -0
- package/dist/types/export/html/template.generated.d.ts +1 -1
- package/dist/types/gjc-runtime/deep-interview-state.d.ts +28 -0
- package/dist/types/gjc-runtime/gc-runtime.d.ts +9 -0
- package/dist/types/gjc-runtime/launch-tmux.d.ts +3 -0
- package/dist/types/gjc-runtime/managed-owner-admission.d.ts +42 -0
- package/dist/types/gjc-runtime/managed-owner-supervisor.d.ts +41 -0
- package/dist/types/gjc-runtime/state-argv.d.ts +38 -0
- package/dist/types/gjc-runtime/tmux-owner-isolation.d.ts +9 -0
- package/dist/types/gjc-runtime/ultragoal-owner-loss-recovery.d.ts +94 -0
- package/dist/types/gjc-runtime/ultragoal-runtime.d.ts +1 -0
- package/dist/types/harness-control-plane/owner.d.ts +3 -1
- package/dist/types/internal-urls/local-protocol.d.ts +18 -0
- package/dist/types/main.d.ts +2 -2
- package/dist/types/modes/components/ansi-display-validator.d.ts +2 -0
- package/dist/types/modes/components/custom-editor.d.ts +8 -3
- package/dist/types/modes/components/notifications-settings-editor.d.ts +4 -0
- package/dist/types/modes/components/session-observer-overlay.d.ts +3 -1
- package/dist/types/modes/components/settings-selector.d.ts +2 -0
- package/dist/types/modes/components/tool-transcript-format.d.ts +77 -0
- package/dist/types/modes/components/transcript-viewer-overlay.d.ts +3 -0
- package/dist/types/modes/controllers/goal-mode-controller.d.ts +8 -1
- package/dist/types/modes/controllers/input-controller.d.ts +8 -2
- package/dist/types/modes/controllers/selector-controller.d.ts +15 -0
- package/dist/types/modes/shared/agent-wire/workflow-gate-broker.d.ts +3 -0
- package/dist/types/modes/shared/agent-wire/workflow-gate-types.d.ts +16 -0
- package/dist/types/plan-mode/approved-plan.d.ts +1 -0
- package/dist/types/runtime-mcp/manager.d.ts +2 -0
- package/dist/types/runtime-mcp/oauth-discovery.d.ts +12 -1
- package/dist/types/sdk/broker/broker.d.ts +1 -0
- package/dist/types/sdk/broker/discovery.d.ts +10 -0
- package/dist/types/sdk/broker/ensure.d.ts +24 -1
- package/dist/types/sdk/broker/internal.d.ts +3 -0
- package/dist/types/sdk/broker/session-index.d.ts +12 -0
- package/dist/types/sdk/broker/state-version.d.ts +2 -1
- package/dist/types/sdk/bus/chat-daemon-cli.d.ts +3 -1
- package/dist/types/sdk/bus/chat-daemon-control.d.ts +39 -4
- package/dist/types/sdk/bus/config-commands.d.ts +8 -0
- package/dist/types/sdk/bus/config.d.ts +22 -0
- package/dist/types/sdk/bus/index.d.ts +162 -2
- package/dist/types/sdk/bus/notification-service.d.ts +71 -8
- package/dist/types/sdk/bus/session-control.d.ts +11 -8
- package/dist/types/sdk/bus/telegram-daemon-cli.d.ts +5 -0
- package/dist/types/sdk/bus/telegram-daemon-contract.d.ts +6 -3
- package/dist/types/sdk/bus/telegram-daemon-control.d.ts +23 -4
- package/dist/types/sdk/bus/telegram-daemon.d.ts +214 -20
- package/dist/types/sdk/session.d.ts +16 -1
- package/dist/types/secrets/index.d.ts +4 -2
- package/dist/types/secrets/obfuscator.d.ts +1 -1
- package/dist/types/session/agent-session.d.ts +31 -2
- package/dist/types/session/artifacts.d.ts +30 -8
- package/dist/types/session/internal/managed-session-scope.d.ts +7 -0
- package/dist/types/session/internal/managed-session-storage.d.ts +63 -6
- package/dist/types/session/session-manager.d.ts +88 -18
- package/dist/types/session/session-storage.d.ts +27 -0
- package/dist/types/session/streaming-output.d.ts +4 -0
- package/dist/types/task/executor.d.ts +9 -0
- package/dist/types/tool-discovery/tool-index.d.ts +2 -0
- package/dist/types/tools/ask.d.ts +87 -12
- package/dist/types/tools/bash-skill-urls.d.ts +2 -2
- package/dist/types/tools/browser/screenshot-format.d.ts +8 -0
- package/dist/types/tools/browser/tab-protocol.d.ts +11 -0
- package/dist/types/tools/browser/tab-supervisor.d.ts +1 -1
- package/dist/types/tools/browser/tab-worker-path-resolver.d.ts +2 -0
- package/dist/types/tools/browser/tab-worker-smoke.d.ts +16 -0
- package/dist/types/tools/index.d.ts +4 -0
- package/dist/types/tools/path-utils.d.ts +9 -14
- package/dist/types/tools/render-utils.d.ts +1 -8
- package/dist/types/tools/resource-gc.d.ts +14 -0
- package/dist/types/utils/image-loading.d.ts +23 -0
- package/dist/types/utils/image-resize.d.ts +2 -0
- package/dist/types/utils/pasted-image-loading.d.ts +39 -0
- package/dist/types/utils/pasted-image-path.d.ts +21 -19
- package/dist/types/web/scrapers/html-to-markdown.d.ts +5 -0
- package/dist/types/web/scrapers/types.d.ts +1 -5
- package/package.json +25 -9
- package/scripts/generate-sdk-operation-inventory.ts +2 -0
- package/scripts/run-threat-model-check.ts +60 -0
- package/src/cli/notify-cli.ts +40 -18
- package/src/cli/worktree-cli.ts +9 -287
- package/src/cli.ts +10 -9
- package/src/commands/gc.ts +11 -1
- package/src/commands/sdk.ts +10 -4
- package/src/commands/worktree.ts +10 -53
- package/src/config/model-profiles.ts +15 -15
- package/src/config/settings-schema.ts +17 -0
- package/src/daemon/runtime.ts +2 -2
- package/src/defaults/gjc/skills/deep-interview/SKILL.md +70 -49
- package/src/defaults/gjc/skills/ralplan/SKILL.md +0 -6
- package/src/defaults/gjc/skills/team/SKILL.md +7 -9
- package/src/defaults/gjc/skills/ultragoal/SKILL.md +0 -2
- package/src/eval/js/shared/runtime.ts +1 -1
- package/src/eval/py/index.ts +2 -2
- package/src/export/html/template.generated.ts +1 -1
- package/src/extensibility/plugins/marketplace/manager.ts +94 -10
- package/src/gjc-runtime/deep-interview-recorder.ts +7 -0
- package/src/gjc-runtime/deep-interview-runtime.ts +10 -0
- package/src/gjc-runtime/deep-interview-state.ts +127 -3
- package/src/gjc-runtime/gc-render.ts +19 -1
- package/src/gjc-runtime/gc-runtime.ts +68 -3
- package/src/gjc-runtime/launch-tmux.ts +61 -6
- package/src/gjc-runtime/managed-owner-admission.ts +352 -0
- package/src/gjc-runtime/managed-owner-supervisor.ts +162 -0
- package/src/gjc-runtime/state-argv.ts +292 -0
- package/src/gjc-runtime/state-runtime.ts +80 -222
- package/src/gjc-runtime/team-runtime.ts +1 -1
- package/src/gjc-runtime/tmux-owner-isolation.ts +117 -0
- package/src/gjc-runtime/ultragoal-owner-loss-recovery.ts +328 -0
- package/src/gjc-runtime/ultragoal-runtime.ts +14 -0
- package/src/harness-control-plane/owner.ts +59 -3
- package/src/hindsight/client.ts +65 -2
- package/src/internal-urls/agent-protocol.ts +76 -5
- package/src/internal-urls/docs-index.generated.ts +7 -6
- package/src/internal-urls/local-protocol.ts +416 -18
- package/src/lsp/index.ts +4 -88
- package/src/main.ts +37 -13
- package/src/modes/acp/acp-agent.ts +8 -0
- package/src/modes/components/ansi-display-validator.ts +54 -0
- package/src/modes/components/custom-editor.ts +103 -32
- package/src/modes/components/custom-provider-wizard.ts +26 -1
- package/src/modes/components/notifications-settings-editor.ts +26 -2
- package/src/modes/components/session-observer-overlay.ts +40 -37
- package/src/modes/components/settings-selector.ts +3 -1
- package/src/modes/components/tool-transcript-format.ts +393 -0
- package/src/modes/components/transcript-viewer-overlay.ts +63 -1
- package/src/modes/controllers/goal-mode-controller.ts +20 -1
- package/src/modes/controllers/input-controller.ts +98 -37
- package/src/modes/controllers/plan-mode-controller.ts +2 -0
- package/src/modes/controllers/runtime-mcp-command-controller.ts +3 -0
- package/src/modes/controllers/selector-controller.ts +71 -9
- package/src/modes/interactive-mode.ts +14 -2
- package/src/modes/shared/agent-wire/deep-interview-gate.ts +31 -1
- package/src/modes/shared/agent-wire/workflow-gate-broker.ts +17 -1
- package/src/modes/shared/agent-wire/workflow-gate-schema.ts +4 -2
- package/src/modes/shared/agent-wire/workflow-gate-types.ts +120 -3
- package/src/plan-mode/approved-plan.ts +3 -1
- package/src/prompts/agents/architect.md +1 -0
- package/src/prompts/agents/critic.md +1 -0
- package/src/prompts/system/custom-system-prompt.md +1 -1
- package/src/prompts/system/system-prompt.md +2 -1
- package/src/prompts/tools/lsp.md +3 -3
- package/src/runtime-mcp/manager.ts +16 -1
- package/src/runtime-mcp/oauth-discovery.ts +116 -26
- package/src/sdk/broker/broker.ts +139 -32
- package/src/sdk/broker/discovery.ts +125 -0
- package/src/sdk/broker/ensure.ts +103 -19
- package/src/sdk/broker/internal.ts +10 -0
- package/src/sdk/broker/session-index.ts +309 -38
- package/src/sdk/broker/state-version.ts +5 -2
- package/src/sdk/bus/chat-daemon-cli.ts +72 -10
- package/src/sdk/bus/chat-daemon-control.ts +526 -130
- package/src/sdk/bus/chat-daemon-runtime.ts +1 -0
- package/src/sdk/bus/config-commands.ts +22 -0
- package/src/sdk/bus/config.ts +37 -0
- package/src/sdk/bus/index.ts +384 -92
- package/src/sdk/bus/lifecycle-control-runtime.ts +53 -3
- package/src/sdk/bus/notification-service.ts +284 -62
- package/src/sdk/bus/recent-activity.ts +14 -5
- package/src/sdk/bus/session-control.ts +150 -44
- package/src/sdk/bus/telegram-daemon-cli.ts +20 -14
- package/src/sdk/bus/telegram-daemon-contract.ts +6 -3
- package/src/sdk/bus/telegram-daemon-control.ts +294 -76
- package/src/sdk/bus/telegram-daemon.ts +1842 -281
- package/src/sdk/protocol/operation-inventory.generated.json +11 -0
- package/src/sdk/session.ts +155 -27
- package/src/secrets/index.ts +53 -10
- package/src/secrets/obfuscator.ts +21 -18
- package/src/secrets/regex.ts +2 -0
- package/src/session/agent-session.ts +495 -253
- package/src/session/artifacts.ts +188 -23
- package/src/session/context-estimation.ts +7 -2
- package/src/session/internal/managed-session-scope.ts +215 -39
- package/src/session/internal/managed-session-storage.ts +1152 -46
- package/src/session/session-manager.ts +1055 -148
- package/src/session/session-storage.ts +109 -11
- package/src/session/streaming-output.ts +6 -0
- package/src/task/executor.ts +71 -26
- package/src/task/index.ts +63 -20
- package/src/tool-discovery/tool-index.ts +18 -0
- package/src/tools/ask.ts +329 -57
- package/src/tools/bash-allowed-prefixes.ts +79 -29
- package/src/tools/bash-skill-urls.ts +14 -3
- package/src/tools/bash.ts +2 -2
- package/src/tools/browser/launch.ts +3 -1
- package/src/tools/browser/readable.ts +1 -1
- package/src/tools/browser/screenshot-format.ts +34 -0
- package/src/tools/browser/tab-protocol.ts +3 -0
- package/src/tools/browser/tab-supervisor.ts +125 -122
- package/src/tools/browser/tab-worker-entry.ts +33 -14
- package/src/tools/browser/tab-worker-path-resolver.ts +67 -0
- package/src/tools/browser/tab-worker-smoke.ts +87 -0
- package/src/tools/browser/tab-worker.ts +7 -5
- package/src/tools/index.ts +4 -0
- package/src/tools/path-utils.ts +42 -35
- package/src/tools/plan-mode-guard.ts +1 -0
- package/src/tools/read.ts +7 -1
- package/src/tools/render-utils.ts +1 -27
- package/src/tools/resource-gc.ts +123 -34
- package/src/tools/sqlite-reader.ts +3 -2
- package/src/tools/subagent.ts +2 -0
- package/src/utils/image-loading.ts +85 -34
- package/src/utils/image-resize.ts +16 -9
- package/src/utils/pasted-image-loading.ts +514 -0
- package/src/utils/pasted-image-path.ts +223 -98
- package/src/web/scrapers/html-to-markdown.ts +68 -0
- package/src/web/scrapers/readthedocs.ts +1 -1
- package/src/web/scrapers/types.ts +1 -68
- package/src/web/search/providers/openai-compatible.ts +76 -23
|
@@ -3,19 +3,38 @@ import type { Transport, WorkerInbound, WorkerOutbound } from "./tab-protocol";
|
|
|
3
3
|
import { WorkerCore } from "./tab-worker";
|
|
4
4
|
|
|
5
5
|
if (!parentPort) throw new Error("tab-worker-entry: missing parentPort");
|
|
6
|
+
const port = parentPort;
|
|
7
|
+
port.once("message", bootstrap => {
|
|
8
|
+
if (!isBootstrap(bootstrap)) {
|
|
9
|
+
port.postMessage({ type: "bootstrap-failed", error: "Invalid tab worker bootstrap." });
|
|
10
|
+
port.close();
|
|
11
|
+
return;
|
|
12
|
+
}
|
|
6
13
|
|
|
7
|
-
const transport: Transport = {
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
};
|
|
14
|
+
const transport: Transport = {
|
|
15
|
+
send(msg, transferList) {
|
|
16
|
+
port.postMessage(msg, transferList ?? []);
|
|
17
|
+
},
|
|
18
|
+
onMessage(handler) {
|
|
19
|
+
const wrap = (message: unknown): void => handler(message as WorkerOutbound | WorkerInbound);
|
|
20
|
+
port.on("message", wrap);
|
|
21
|
+
return () => port.off("message", wrap);
|
|
22
|
+
},
|
|
23
|
+
close() {
|
|
24
|
+
port.close();
|
|
25
|
+
},
|
|
26
|
+
};
|
|
20
27
|
|
|
21
|
-
new WorkerCore(transport);
|
|
28
|
+
new WorkerCore(transport);
|
|
29
|
+
port.postMessage({ type: "bootstrap-ready", version: 1, mode: "native-free" });
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
function isBootstrap(message: unknown): message is Extract<WorkerInbound, { type: "bootstrap" }> {
|
|
33
|
+
return (
|
|
34
|
+
typeof message === "object" &&
|
|
35
|
+
message !== null &&
|
|
36
|
+
(message as { type?: unknown }).type === "bootstrap" &&
|
|
37
|
+
(message as { version?: unknown }).version === 1 &&
|
|
38
|
+
(message as { mode?: unknown }).mode === "native-free"
|
|
39
|
+
);
|
|
40
|
+
}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import * as os from "node:os";
|
|
2
|
+
import * as path from "node:path";
|
|
3
|
+
import * as url from "node:url";
|
|
4
|
+
|
|
5
|
+
const UNICODE_SPACES = /[\u00A0\u2000-\u200A\u202F\u205F\u3000]/g;
|
|
6
|
+
const TOP_LEVEL_INTERNAL_URL_PREFIXES = ["agent://", "artifact://", "rule://", "local://"] as const;
|
|
7
|
+
|
|
8
|
+
function normalizeAtPrefix(filePath: string): string {
|
|
9
|
+
if (!filePath.startsWith("@")) return filePath;
|
|
10
|
+
|
|
11
|
+
const withoutAt = filePath.slice(1);
|
|
12
|
+
if (
|
|
13
|
+
withoutAt.startsWith("/") ||
|
|
14
|
+
withoutAt === "~" ||
|
|
15
|
+
withoutAt.startsWith("~/") ||
|
|
16
|
+
path.win32.isAbsolute(withoutAt) ||
|
|
17
|
+
withoutAt.startsWith("agent://") ||
|
|
18
|
+
withoutAt.startsWith("artifact://") ||
|
|
19
|
+
withoutAt.startsWith("rule://") ||
|
|
20
|
+
withoutAt.startsWith("local:")
|
|
21
|
+
) {
|
|
22
|
+
return withoutAt;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
return filePath;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function expandPath(filePath: string): string {
|
|
29
|
+
const normalized = normalizeAtPrefix(filePath).replace(UNICODE_SPACES, " ");
|
|
30
|
+
if (normalized.toLowerCase().startsWith("file://")) {
|
|
31
|
+
try {
|
|
32
|
+
return expandTilde(url.fileURLToPath(normalized));
|
|
33
|
+
} catch {
|
|
34
|
+
return normalized;
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
return expandTilde(normalized);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function expandTilde(filePath: string): string {
|
|
41
|
+
const home = os.homedir();
|
|
42
|
+
if (filePath === "~") return home;
|
|
43
|
+
if (filePath.startsWith("~/") || filePath.startsWith("~\\")) return home + filePath.slice(1);
|
|
44
|
+
if (filePath.startsWith("~")) return path.join(home, filePath.slice(1));
|
|
45
|
+
return filePath;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
function normalizeLocalScheme(filePath: string): string {
|
|
49
|
+
return filePath.replace(/^(local:)\/(?!\/)/, "$1//");
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/** Resolve filesystem-only worker inputs without evaluating the broad path utility module. */
|
|
53
|
+
export function resolveTabWorkerPath(filePath: string, cwd: string): string {
|
|
54
|
+
const normalized = normalizeLocalScheme(filePath);
|
|
55
|
+
const expanded = expandPath(normalized);
|
|
56
|
+
const expandedAndNormalized = normalizeLocalScheme(expanded);
|
|
57
|
+
for (const prefix of TOP_LEVEL_INTERNAL_URL_PREFIXES) {
|
|
58
|
+
if (expandedAndNormalized.startsWith(prefix)) {
|
|
59
|
+
throw new Error(
|
|
60
|
+
`Path "${normalized}" uses internal scheme "${prefix}" and must be resolved through the proper protocol handler, not as a filesystem path.`,
|
|
61
|
+
);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
if (/^\/+$/u.test(expanded)) return cwd;
|
|
66
|
+
return path.isAbsolute(expanded) ? expanded : path.resolve(cwd, expanded);
|
|
67
|
+
}
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import { isCompiledBinary } from "@gajae-code/utils/env";
|
|
2
|
+
import type { WorkerInbound, WorkerOutbound } from "./tab-protocol";
|
|
3
|
+
|
|
4
|
+
interface SmokeWorkerHandle {
|
|
5
|
+
postMessage(message: WorkerInbound): void;
|
|
6
|
+
addEventListener(type: string, listener: EventListener): void;
|
|
7
|
+
removeEventListener(type: string, listener: EventListener): void;
|
|
8
|
+
terminate(): void;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
const SMOKE_TIMEOUT_MS = 5_000;
|
|
12
|
+
const TAB_WORKER_MODE = "native-free";
|
|
13
|
+
|
|
14
|
+
function smokeStartupError(stage: string): Error {
|
|
15
|
+
return new Error(
|
|
16
|
+
`Tab worker startup failed (stage=${stage}, mode=${TAB_WORKER_MODE}, platform=${process.platform}).`,
|
|
17
|
+
);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Starts the real tab worker entry without connecting to a browser, then proves
|
|
22
|
+
* its bootstrap and shutdown protocol. The compiled branch deliberately keeps
|
|
23
|
+
* a static literal for Bun's --compile worker discovery.
|
|
24
|
+
*/
|
|
25
|
+
export async function smokeTestTabWorker(timeoutMs = SMOKE_TIMEOUT_MS): Promise<void> {
|
|
26
|
+
const worker = isCompiledBinary()
|
|
27
|
+
? new Worker("./packages/coding-agent/src/tools/browser/tab-worker-entry.ts", { type: "module" })
|
|
28
|
+
: new Worker(new URL("./tab-worker-entry.ts", import.meta.url).href, { type: "module" });
|
|
29
|
+
await smokeTestTabWorkerWithWorkerForTest(worker, timeoutMs);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/** Test-only: verify the smoke handshake against a controlled worker transport. */
|
|
33
|
+
export async function smokeTestTabWorkerWithWorkerForTest(worker: SmokeWorkerHandle, timeoutMs: number): Promise<void> {
|
|
34
|
+
type SmokePhase = "await-bootstrap" | "await-closed";
|
|
35
|
+
let phase: SmokePhase = "await-bootstrap";
|
|
36
|
+
const { promise, resolve, reject } = Promise.withResolvers<void>();
|
|
37
|
+
let settled = false;
|
|
38
|
+
let timeout: NodeJS.Timeout | undefined;
|
|
39
|
+
|
|
40
|
+
const cleanup = (): void => {
|
|
41
|
+
if (timeout) clearTimeout(timeout);
|
|
42
|
+
worker.removeEventListener("message", onMessage);
|
|
43
|
+
worker.removeEventListener("error", onError);
|
|
44
|
+
worker.removeEventListener("messageerror", onMessageError);
|
|
45
|
+
};
|
|
46
|
+
const succeed = (): void => {
|
|
47
|
+
if (settled) return;
|
|
48
|
+
settled = true;
|
|
49
|
+
resolve();
|
|
50
|
+
};
|
|
51
|
+
const fail = (stage: string): void => {
|
|
52
|
+
if (settled) return;
|
|
53
|
+
settled = true;
|
|
54
|
+
reject(smokeStartupError(stage));
|
|
55
|
+
};
|
|
56
|
+
const onMessage: EventListener = event => {
|
|
57
|
+
const message = (event as MessageEvent<WorkerOutbound>).data;
|
|
58
|
+
if (message.type === "log") return;
|
|
59
|
+
if (phase === "await-bootstrap") {
|
|
60
|
+
if (message.type === "bootstrap-ready" && message.version === 1 && message.mode === TAB_WORKER_MODE) {
|
|
61
|
+
phase = "await-closed";
|
|
62
|
+
worker.postMessage({ type: "close" } satisfies WorkerInbound);
|
|
63
|
+
} else if (message.type === "bootstrap-failed") {
|
|
64
|
+
fail("bootstrap");
|
|
65
|
+
} else {
|
|
66
|
+
fail("protocol-phase");
|
|
67
|
+
}
|
|
68
|
+
return;
|
|
69
|
+
}
|
|
70
|
+
if (message.type === "closed") succeed();
|
|
71
|
+
else fail("protocol-phase");
|
|
72
|
+
};
|
|
73
|
+
const onError: EventListener = () => fail("error");
|
|
74
|
+
const onMessageError: EventListener = () => fail("messageerror");
|
|
75
|
+
|
|
76
|
+
worker.addEventListener("message", onMessage);
|
|
77
|
+
worker.addEventListener("error", onError);
|
|
78
|
+
worker.addEventListener("messageerror", onMessageError);
|
|
79
|
+
timeout = setTimeout(() => fail("smoke-timeout"), timeoutMs);
|
|
80
|
+
try {
|
|
81
|
+
worker.postMessage({ type: "bootstrap", version: 1, mode: TAB_WORKER_MODE } satisfies WorkerInbound);
|
|
82
|
+
await promise;
|
|
83
|
+
} finally {
|
|
84
|
+
cleanup();
|
|
85
|
+
worker.terminate();
|
|
86
|
+
}
|
|
87
|
+
}
|
|
@@ -2,7 +2,8 @@ import * as fs from "node:fs";
|
|
|
2
2
|
import * as os from "node:os";
|
|
3
3
|
import * as path from "node:path";
|
|
4
4
|
|
|
5
|
-
import {
|
|
5
|
+
import { untilAborted } from "@gajae-code/utils/abortable";
|
|
6
|
+
import { Snowflake } from "@gajae-code/utils/snowflake";
|
|
6
7
|
import type { HTMLElement } from "linkedom";
|
|
7
8
|
import type {
|
|
8
9
|
Browser,
|
|
@@ -17,8 +18,6 @@ import type {
|
|
|
17
18
|
import { JsRuntime, type RuntimeHooks } from "../../eval/js/shared/runtime";
|
|
18
19
|
import type { JsDisplayOutput } from "../../eval/js/shared/types";
|
|
19
20
|
import { resizeImage } from "../../utils/image-resize";
|
|
20
|
-
import { resolveToCwd } from "../path-utils";
|
|
21
|
-
import { formatScreenshot } from "../render-utils";
|
|
22
21
|
import { ToolAbortError, ToolError, throwIfAborted } from "../tool-errors";
|
|
23
22
|
import {
|
|
24
23
|
applyStealthPatches,
|
|
@@ -28,6 +27,7 @@ import {
|
|
|
28
27
|
loadPuppeteerInWorker,
|
|
29
28
|
} from "./launch";
|
|
30
29
|
import { extractReadableFromHtml, type ReadableFormat, type ReadableResult } from "./readable";
|
|
30
|
+
import { formatScreenshot } from "./screenshot-format";
|
|
31
31
|
import type {
|
|
32
32
|
Observation,
|
|
33
33
|
ObservationEntry,
|
|
@@ -41,6 +41,7 @@ import type {
|
|
|
41
41
|
WorkerInbound,
|
|
42
42
|
WorkerInitPayload,
|
|
43
43
|
} from "./tab-protocol";
|
|
44
|
+
import { resolveTabWorkerPath } from "./tab-worker-path-resolver";
|
|
44
45
|
|
|
45
46
|
declare global {
|
|
46
47
|
interface Element extends HTMLElement {}
|
|
@@ -813,7 +814,8 @@ export class WorkerCore {
|
|
|
813
814
|
{ type: "image", data: buffer.toBase64(), mimeType: "image/png" },
|
|
814
815
|
{ maxWidth: 1024, maxHeight: 1024, maxBytes: 150 * 1024, jpegQuality: 70 },
|
|
815
816
|
);
|
|
816
|
-
const explicitPath = opts.save ?
|
|
817
|
+
const explicitPath = opts.save ? resolveTabWorkerPath(opts.save, session.cwd) : undefined;
|
|
818
|
+
|
|
817
819
|
const dest =
|
|
818
820
|
explicitPath ??
|
|
819
821
|
(session.browserScreenshotDir
|
|
@@ -950,7 +952,7 @@ export class WorkerCore {
|
|
|
950
952
|
page.locator(normalizeSelector(selector)).setTimeout(timeoutMs).waitHandle(),
|
|
951
953
|
)) as ElementHandle;
|
|
952
954
|
try {
|
|
953
|
-
const absolute = filePaths.map(filePath =>
|
|
955
|
+
const absolute = filePaths.map(filePath => resolveTabWorkerPath(filePath, session.cwd));
|
|
954
956
|
const upload = handle as unknown as { uploadFile: (...paths: string[]) => Promise<void> };
|
|
955
957
|
const tagName = (await untilAborted(signal, () =>
|
|
956
958
|
handle.evaluate(el => (el as unknown as { tagName: string }).tagName),
|
package/src/tools/index.ts
CHANGED
|
@@ -216,6 +216,8 @@ export interface ToolSession {
|
|
|
216
216
|
cwd: string;
|
|
217
217
|
/** Whether UI is available */
|
|
218
218
|
hasUI: boolean;
|
|
219
|
+
/** Whether this session will bind a workflow-gate emitter after tool construction. */
|
|
220
|
+
workflowGateEligible?: boolean;
|
|
219
221
|
/** Skip Python kernel availability check and warmup */
|
|
220
222
|
skipPythonPreflight?: boolean;
|
|
221
223
|
/** Pre-loaded context files (AGENTS.md, etc) */
|
|
@@ -262,6 +264,8 @@ export interface ToolSession {
|
|
|
262
264
|
requestForegroundBashBackground?: () => boolean;
|
|
263
265
|
/** Get session ID */
|
|
264
266
|
getSessionId?: () => string | null;
|
|
267
|
+
/** Whether local:// must use external managed scratch instead of artifacts/local. */
|
|
268
|
+
isManagedSessionDestination?: () => boolean;
|
|
265
269
|
/** Get Hindsight runtime state for this agent session. */
|
|
266
270
|
getHindsightSessionState?: () => HindsightSessionState | undefined;
|
|
267
271
|
/** Agent identity used for IRC routing. Returns the registry id (e.g. "0-Main", "0-AuthLoader"). */
|
package/src/tools/path-utils.ts
CHANGED
|
@@ -2,7 +2,7 @@ import * as fs from "node:fs";
|
|
|
2
2
|
import * as os from "node:os";
|
|
3
3
|
import * as path from "node:path";
|
|
4
4
|
import * as url from "node:url";
|
|
5
|
-
import { isEnoent } from "@gajae-code/utils";
|
|
5
|
+
import { isEnoent } from "@gajae-code/utils/fs-error";
|
|
6
6
|
import { InternalUrlRouter } from "../internal-urls";
|
|
7
7
|
import { ToolError } from "./tool-errors";
|
|
8
8
|
|
|
@@ -10,13 +10,9 @@ const UNICODE_SPACES = /[\u00A0\u2000-\u200A\u202F\u205F\u3000]/g;
|
|
|
10
10
|
const FILE_LINE_RANGE_RE = /^(?:L?\d+(?:[-+]L?\d+|-)?(?:,L?\d+(?:[-+]L?\d+|-)?)*|raw|conflicts)$/i;
|
|
11
11
|
const FILE_LINE_RANGE_ONLY_RE = /^L?\d+(?:[-+]L?\d+|-)?(?:,L?\d+(?:[-+]L?\d+|-)?)*$/i;
|
|
12
12
|
const FILE_RAW_ONLY_RE = /^raw$/i;
|
|
13
|
-
//
|
|
14
|
-
//
|
|
15
|
-
//
|
|
16
|
-
const INTERNAL_URL_SELECTOR_PART_RE =
|
|
17
|
-
/^(?:raw|conflicts|L?\d+(?:[-+]L?\d+|-)?(?:,L?\d+(?:[-+]L?\d+|-)?)*|-\d+(?:[-+]\d+)?)$/i;
|
|
18
|
-
// Schemes whose host grammar is identifier-shaped, so any trailing
|
|
19
|
-
// `:<selector-chunk>` is unambiguously a read-tool selector.
|
|
13
|
+
// Schemes whose authority grammar is identifier-shaped and may therefore carry
|
|
14
|
+
// a read selector immediately after the authority. Colons in a path, query, or
|
|
15
|
+
// fragment remain part of the URL and are never considered selectors.
|
|
20
16
|
const INTERNAL_SCHEMES_WITH_SELECTORS: Record<string, true> = {
|
|
21
17
|
agent: true,
|
|
22
18
|
artifact: true,
|
|
@@ -148,39 +144,50 @@ export function splitPathAndSel(rawPath: string): { path: string; sel?: string }
|
|
|
148
144
|
/**
|
|
149
145
|
* Variant of {@link splitPathAndSel} for internal URLs (`scheme://...`).
|
|
150
146
|
*
|
|
151
|
-
*
|
|
152
|
-
*
|
|
153
|
-
*
|
|
154
|
-
*
|
|
155
|
-
*
|
|
156
|
-
*
|
|
157
|
-
*
|
|
158
|
-
* This function iteratively peels selector-shaped chunks (well-formed plus
|
|
159
|
-
* common malformed shapes like `:-N`) so the rest of the read tool can pass a
|
|
160
|
-
* clean URL to the protocol handler and surface selector errors via parseSel
|
|
161
|
-
* instead of as misleading "host invalid" errors from the handler.
|
|
162
|
-
*
|
|
163
|
-
* Falls back to the input unchanged when nothing matches.
|
|
147
|
+
* Artifact authorities are generated identifiers, so their complete tail is
|
|
148
|
+
* unambiguously a selector and malformed selectors can be rejected before
|
|
149
|
+
* resolving the artifact. Other resources may legitimately use colons in
|
|
150
|
+
* their identities, so they only lose strict, recognized selector suffixes.
|
|
151
|
+
* Skill authorities additionally use the active registry to distinguish a
|
|
152
|
+
* skill name from its selector without making path-utils depend on skills.
|
|
164
153
|
*/
|
|
165
|
-
export function splitInternalUrlSel(
|
|
154
|
+
export function splitInternalUrlSel(
|
|
155
|
+
rawPath: string,
|
|
156
|
+
options: { activeSkillNames?: readonly string[] } = {},
|
|
157
|
+
): { path: string; sel?: string } {
|
|
166
158
|
const schemeMatch = rawPath.match(INTERNAL_URL_SCHEME_RE);
|
|
167
159
|
if (!schemeMatch) return { path: rawPath };
|
|
168
|
-
|
|
160
|
+
const scheme = schemeMatch[1].toLowerCase();
|
|
161
|
+
if (!INTERNAL_SCHEMES_WITH_SELECTORS[scheme]) return { path: rawPath };
|
|
169
162
|
|
|
170
163
|
const schemeEnd = schemeMatch[0].length;
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
164
|
+
const authorityTerminator = rawPath.slice(schemeEnd).search(/[/?#]/);
|
|
165
|
+
if (authorityTerminator !== -1) return { path: rawPath };
|
|
166
|
+
const authority = rawPath.slice(schemeEnd);
|
|
167
|
+
const firstColon = authority.indexOf(":");
|
|
168
|
+
if (firstColon === -1) return { path: rawPath };
|
|
169
|
+
|
|
170
|
+
if (scheme === "artifact") {
|
|
171
|
+
return { path: rawPath.slice(0, schemeEnd + firstColon), sel: authority.slice(firstColon + 1) };
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
if (scheme === "skill") {
|
|
175
|
+
const activeSkillNames = options.activeSkillNames ?? [];
|
|
176
|
+
if (activeSkillNames.includes(authority)) return { path: rawPath };
|
|
177
|
+
const skillName = activeSkillNames
|
|
178
|
+
.filter(name => authority.startsWith(`${name}:`))
|
|
179
|
+
.sort((a, b) => b.length - a.length)[0];
|
|
180
|
+
if (skillName) {
|
|
181
|
+
return {
|
|
182
|
+
path: `${rawPath.slice(0, schemeEnd)}${skillName}`,
|
|
183
|
+
sel: authority.slice(skillName.length + 1),
|
|
184
|
+
};
|
|
185
|
+
}
|
|
181
186
|
}
|
|
182
|
-
|
|
183
|
-
|
|
187
|
+
|
|
188
|
+
const strict = splitPathAndSel(authority);
|
|
189
|
+
if (strict.sel === undefined) return { path: rawPath };
|
|
190
|
+
return { path: `${rawPath.slice(0, schemeEnd)}${strict.path}`, sel: strict.sel };
|
|
184
191
|
}
|
|
185
192
|
|
|
186
193
|
function assertNotInternalUrl(expanded: string, original: string): void {
|
|
@@ -11,6 +11,7 @@ function resolveRawPath(session: ToolSession, targetPath: string): string {
|
|
|
11
11
|
if (normalized.startsWith(LOCAL_SCHEME_PREFIX)) {
|
|
12
12
|
return resolveLocalUrlToPath(normalized, {
|
|
13
13
|
getArtifactsDir: session.getArtifactsDir,
|
|
14
|
+
isManagedDestination: session.isManagedSessionDestination,
|
|
14
15
|
getSessionId: session.getSessionId,
|
|
15
16
|
});
|
|
16
17
|
}
|
package/src/tools/read.ts
CHANGED
|
@@ -11,6 +11,7 @@ import * as z from "zod/v4";
|
|
|
11
11
|
import { getFileReadCache } from "../edit/file-read-cache";
|
|
12
12
|
import { isNotebookPath, readEditableNotebookText } from "../edit/notebook";
|
|
13
13
|
import type { RenderResultOptions } from "../extensibility/custom-tools/types";
|
|
14
|
+
import { getActiveSkills } from "../extensibility/skills";
|
|
14
15
|
import { formatHashLine, formatHashLines, formatLineHash, HL_BODY_SEP } from "../hashline/hash";
|
|
15
16
|
import { InternalUrlRouter } from "../internal-urls";
|
|
16
17
|
import { parseInternalUrl } from "../internal-urls/parse";
|
|
@@ -1585,8 +1586,13 @@ export class ReadTool implements AgentTool<typeof readSchema, ReadToolDetails> {
|
|
|
1585
1586
|
// off the URL and surfaced via parseSel rather than confusing handlers.
|
|
1586
1587
|
const internalRouter = InternalUrlRouter.instance();
|
|
1587
1588
|
if (internalRouter.canHandle(readPath)) {
|
|
1588
|
-
const internalTarget = splitInternalUrlSel(readPath
|
|
1589
|
+
const internalTarget = splitInternalUrlSel(readPath, {
|
|
1590
|
+
activeSkillNames: getActiveSkills().map(skill => skill.name),
|
|
1591
|
+
});
|
|
1589
1592
|
const parsed = parseSel(internalTarget.sel);
|
|
1593
|
+
if (internalTarget.sel !== undefined && parsed.kind === "none") {
|
|
1594
|
+
throw new ToolError(`Invalid internal URL selector "${internalTarget.sel}".`);
|
|
1595
|
+
}
|
|
1590
1596
|
return this.#handleInternalUrl(internalTarget.path, parsed, signal);
|
|
1591
1597
|
}
|
|
1592
1598
|
|
|
@@ -15,10 +15,10 @@ import { pluralize } from "@gajae-code/utils";
|
|
|
15
15
|
import { settings } from "../config/settings";
|
|
16
16
|
import type { Theme } from "../modes/theme/theme";
|
|
17
17
|
import { Hasher } from "../tui/utils";
|
|
18
|
-
import { formatDimensionNote, type ResizedImage } from "../utils/image-resize";
|
|
19
18
|
|
|
20
19
|
export { Ellipsis } from "@gajae-code/natives";
|
|
21
20
|
export { replaceTabs, truncateToWidth, wrapTextWithAnsi } from "@gajae-code/tui";
|
|
21
|
+
export { formatScreenshot } from "./browser/screenshot-format";
|
|
22
22
|
|
|
23
23
|
// =============================================================================
|
|
24
24
|
// Standardized Display Constants
|
|
@@ -591,32 +591,6 @@ export function formatToolWorkingDirectory(workdir: string | undefined, projectD
|
|
|
591
591
|
return replaceTabs(displayWorkdir);
|
|
592
592
|
}
|
|
593
593
|
|
|
594
|
-
export function formatScreenshot(opts: {
|
|
595
|
-
saveFullRes: boolean;
|
|
596
|
-
savedMimeType: string;
|
|
597
|
-
savedByteLength: number;
|
|
598
|
-
dest: string;
|
|
599
|
-
resized: ResizedImage;
|
|
600
|
-
}): string[] {
|
|
601
|
-
const lines = ["Screenshot captured"];
|
|
602
|
-
if (opts.saveFullRes) {
|
|
603
|
-
lines.push(
|
|
604
|
-
`Saved: ${opts.savedMimeType} (${(opts.savedByteLength / 1024).toFixed(2)} KB) to ${shortenPath(opts.dest)}`,
|
|
605
|
-
);
|
|
606
|
-
lines.push(
|
|
607
|
-
`Model: ${opts.resized.mimeType} (${(opts.resized.buffer.length / 1024).toFixed(2)} KB, ${opts.resized.width}x${opts.resized.height})`,
|
|
608
|
-
);
|
|
609
|
-
} else {
|
|
610
|
-
lines.push(`Format: ${opts.resized.mimeType} (${(opts.resized.buffer.length / 1024).toFixed(2)} KB)`);
|
|
611
|
-
lines.push(`Dimensions: ${opts.resized.width}x${opts.resized.height}`);
|
|
612
|
-
}
|
|
613
|
-
const dimensionNote = formatDimensionNote(opts.resized);
|
|
614
|
-
if (dimensionNote) {
|
|
615
|
-
lines.push(dimensionNote);
|
|
616
|
-
}
|
|
617
|
-
return lines;
|
|
618
|
-
}
|
|
619
|
-
|
|
620
594
|
export function wrapBrackets(text: string, theme: Theme): string {
|
|
621
595
|
return `${theme.format.bracketLeft}${text}${theme.format.bracketRight}`;
|
|
622
596
|
}
|