@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
|
@@ -1,3 +1,11 @@
|
|
|
1
|
+
import {
|
|
2
|
+
classifyStateArgv,
|
|
3
|
+
STATE_ACTION_NAMES,
|
|
4
|
+
type StateAction,
|
|
5
|
+
type StateArgvClassification,
|
|
6
|
+
} from "../gjc-runtime/state-argv";
|
|
7
|
+
import { CANONICAL_GJC_WORKFLOW_SKILLS } from "../skill-state/canonical-skills";
|
|
8
|
+
|
|
1
9
|
export interface BashAllowedPrefixesCheck {
|
|
2
10
|
allowed: boolean;
|
|
3
11
|
reason?: string;
|
|
@@ -11,9 +19,8 @@ export interface BashRestrictionOptions {
|
|
|
11
19
|
|
|
12
20
|
const SHELL_CONTROL_CHARS = new Set([";", "|", "&", "<", ">", "(", ")"]);
|
|
13
21
|
const UNSAFE_UNQUOTED_EXPANSION_CHARS = new Set(["$", "*", "?", "[", "]", "{", "}", "~"]);
|
|
14
|
-
const STATE_FLAGS_WITH_VALUES = new Set(["--input", "--mode", "--session-id", "--thread-id", "--turn-id", "--to"]);
|
|
15
|
-
const STATE_ACTIONS = new Set(["read", "write", "clear", "contract", "handoff"]);
|
|
16
22
|
const ALLOWED_STATE_ACTIONS = new Set(["read", "write", "contract"]);
|
|
23
|
+
const CANONICAL_STATE_TARGETS = new Set<string>(CANONICAL_GJC_WORKFLOW_SKILLS);
|
|
17
24
|
const READ_ONLY_COMMANDS = new Set(["grep", "rg", "tree", "ls", "pwd", "wc", "du", "file", "stat"]);
|
|
18
25
|
|
|
19
26
|
function parseShellWords(command: string): { words: string[]; reason?: string } {
|
|
@@ -21,6 +28,7 @@ function parseShellWords(command: string): { words: string[]; reason?: string }
|
|
|
21
28
|
let current = "";
|
|
22
29
|
let quote: "single" | "double" | null = null;
|
|
23
30
|
|
|
31
|
+
let wordStarted = false;
|
|
24
32
|
for (let index = 0; index < command.length; index += 1) {
|
|
25
33
|
const char = command[index]!;
|
|
26
34
|
const next = command[index + 1];
|
|
@@ -54,10 +62,12 @@ function parseShellWords(command: string): { words: string[]; reason?: string }
|
|
|
54
62
|
|
|
55
63
|
if (char === "'") {
|
|
56
64
|
quote = "single";
|
|
65
|
+
wordStarted = true;
|
|
57
66
|
continue;
|
|
58
67
|
}
|
|
59
68
|
if (char === '"') {
|
|
60
69
|
quote = "double";
|
|
70
|
+
wordStarted = true;
|
|
61
71
|
continue;
|
|
62
72
|
}
|
|
63
73
|
if (char === "`" || (char === "$" && next === "(")) {
|
|
@@ -73,9 +83,10 @@ function parseShellWords(command: string): { words: string[]; reason?: string }
|
|
|
73
83
|
return { words, reason: `shell expansion character '${char}' is not allowed in restricted bash commands` };
|
|
74
84
|
}
|
|
75
85
|
if (/\s/u.test(char)) {
|
|
76
|
-
if (
|
|
86
|
+
if (wordStarted) {
|
|
77
87
|
words.push(current);
|
|
78
88
|
current = "";
|
|
89
|
+
wordStarted = false;
|
|
79
90
|
}
|
|
80
91
|
continue;
|
|
81
92
|
}
|
|
@@ -83,12 +94,13 @@ function parseShellWords(command: string): { words: string[]; reason?: string }
|
|
|
83
94
|
return { words, reason: "backslash escapes are not allowed in restricted bash commands" };
|
|
84
95
|
}
|
|
85
96
|
current += char;
|
|
97
|
+
wordStarted = true;
|
|
86
98
|
}
|
|
87
99
|
|
|
88
100
|
if (quote !== null) {
|
|
89
101
|
return { words, reason: "unterminated quote in restricted bash command" };
|
|
90
102
|
}
|
|
91
|
-
if (
|
|
103
|
+
if (wordStarted) words.push(current);
|
|
92
104
|
return { words };
|
|
93
105
|
}
|
|
94
106
|
|
|
@@ -101,28 +113,62 @@ function wordsStartWith(words: readonly string[], prefix: readonly string[]): bo
|
|
|
101
113
|
return prefix.every((word, index) => words[index] === word);
|
|
102
114
|
}
|
|
103
115
|
|
|
104
|
-
function
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
116
|
+
function malformedStateShape(): StateActionClassification {
|
|
117
|
+
return { reason: "restricted role-agent bash only allows documented `gjc state` action shapes" };
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
interface ParsedStateAction {
|
|
121
|
+
action: string;
|
|
122
|
+
target: string;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
interface StateActionClassification {
|
|
126
|
+
parsed?: ParsedStateAction;
|
|
127
|
+
reason?: string;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
function hasDocumentedStatePositionals(classification: StateArgvClassification): boolean {
|
|
131
|
+
const [first, second, third] = classification.positionals;
|
|
132
|
+
if (third) return false;
|
|
133
|
+
if (!second) return true;
|
|
134
|
+
return STATE_ACTION_NAMES.has(first as StateAction) || STATE_ACTION_NAMES.has(second as StateAction);
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
function classifyStateAction(words: readonly string[]): StateActionClassification {
|
|
138
|
+
const classification = classifyStateArgv(words.slice(2));
|
|
139
|
+
if (
|
|
140
|
+
classification.unknownFlags.length > 0 ||
|
|
141
|
+
classification.flags.some(flag => flag.malformed) ||
|
|
142
|
+
!hasDocumentedStatePositionals(classification)
|
|
143
|
+
) {
|
|
144
|
+
return malformedStateShape();
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
const modeFlags = classification.flags.filter(flag => flag.name === "--mode");
|
|
148
|
+
const inputFlags = classification.flags.filter(flag => flag.name === "--input");
|
|
149
|
+
if (modeFlags.length > 1 || inputFlags.length > 1) {
|
|
150
|
+
return { reason: "restricted role-agent bash rejects repeated or conflicting `gjc state` target selectors" };
|
|
151
|
+
}
|
|
152
|
+
if (classification.inputs.some(input => input.kind === "file")) {
|
|
153
|
+
return { reason: "restricted role-agent bash does not allow file-backed `gjc state --input` values" };
|
|
118
154
|
}
|
|
155
|
+
if (classification.inputs.some(input => input.kind === "invalid")) return malformedStateShape();
|
|
119
156
|
|
|
120
|
-
const
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
157
|
+
const runtimeTarget = classification.runtimeSelectorCandidates.find(candidate => candidate.value)?.value;
|
|
158
|
+
const suppliedTargets = classification.selectorCandidates
|
|
159
|
+
.map(candidate => candidate.value)
|
|
160
|
+
.filter((target): target is string => target !== undefined);
|
|
161
|
+
const distinctTargets = new Set(suppliedTargets);
|
|
162
|
+
if (distinctTargets.size > 1 || (runtimeTarget && suppliedTargets.some(target => target !== runtimeTarget))) {
|
|
163
|
+
return {
|
|
164
|
+
reason:
|
|
165
|
+
"restricted role-agent bash rejects conflicting `gjc state` selectors that disagree with runtime precedence",
|
|
166
|
+
};
|
|
167
|
+
}
|
|
168
|
+
if (!runtimeTarget || !CANONICAL_STATE_TARGETS.has(runtimeTarget)) {
|
|
169
|
+
return { reason: "restricted role-agent bash requires a canonical workflow skill for `gjc state` commands" };
|
|
170
|
+
}
|
|
171
|
+
return { parsed: { action: classification.effectiveAction, target: runtimeTarget } };
|
|
126
172
|
}
|
|
127
173
|
|
|
128
174
|
function optionWords(words: readonly string[]): string[] {
|
|
@@ -189,15 +235,19 @@ function validateMatchedGjcCommand(words: readonly string[]): BashAllowedPrefixe
|
|
|
189
235
|
}
|
|
190
236
|
|
|
191
237
|
if (words[1] === "state") {
|
|
192
|
-
const
|
|
193
|
-
if (!
|
|
238
|
+
const classification = classifyStateAction(words);
|
|
239
|
+
if (!classification.parsed) {
|
|
194
240
|
return {
|
|
195
241
|
allowed: false,
|
|
196
|
-
reason:
|
|
242
|
+
reason:
|
|
243
|
+
classification.reason ?? "restricted role-agent bash only allows documented `gjc state` action shapes",
|
|
197
244
|
};
|
|
198
245
|
}
|
|
199
|
-
if (!ALLOWED_STATE_ACTIONS.has(action)) {
|
|
200
|
-
return {
|
|
246
|
+
if (!ALLOWED_STATE_ACTIONS.has(classification.parsed.action)) {
|
|
247
|
+
return {
|
|
248
|
+
allowed: false,
|
|
249
|
+
reason: `restricted role-agent bash does not allow \`gjc state ${classification.parsed.action}\``,
|
|
250
|
+
};
|
|
201
251
|
}
|
|
202
252
|
return { allowed: true };
|
|
203
253
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import * as fs from "node:fs/promises";
|
|
2
2
|
import * as path from "node:path";
|
|
3
3
|
import type { Skill } from "../extensibility/skills";
|
|
4
|
-
import { type LocalProtocolOptions, resolveLocalUrlToPath } from "../internal-urls";
|
|
4
|
+
import { initializeLocalRoot, type LocalProtocolOptions, resolveLocalUrlToPath } from "../internal-urls";
|
|
5
5
|
import { validateRelativePath } from "../internal-urls/skill-protocol";
|
|
6
6
|
import type { InternalResource } from "../internal-urls/types";
|
|
7
7
|
import { normalizeLocalScheme } from "./path-utils";
|
|
@@ -162,12 +162,23 @@ async function resolveInternalUrlToPath(
|
|
|
162
162
|
return resolveSkillUrlToPath(url, skills);
|
|
163
163
|
}
|
|
164
164
|
|
|
165
|
+
// Managed artifact and agent resources carry session authority. Expanding
|
|
166
|
+
// either into a subprocess command would let that subprocess reopen, replace,
|
|
167
|
+
// or unlink the managed object by pathname. Keep those resources behind the
|
|
168
|
+
// internal reader boundary.
|
|
169
|
+
if (scheme === "artifact" || scheme === "agent") {
|
|
170
|
+
throw new ToolError(
|
|
171
|
+
`${scheme}:// URLs cannot be expanded in bash commands; read them through the internal reader instead.`,
|
|
172
|
+
);
|
|
173
|
+
}
|
|
174
|
+
|
|
165
175
|
if (scheme === "local") {
|
|
166
176
|
if (!localOptions) {
|
|
167
177
|
throw new ToolError(
|
|
168
178
|
"Cannot resolve local:// URL in bash command: local protocol options are unavailable for this session.",
|
|
169
179
|
);
|
|
170
180
|
}
|
|
181
|
+
await initializeLocalRoot(localOptions);
|
|
171
182
|
const resolvedLocalPath = resolveLocalUrlToPath(url, localOptions);
|
|
172
183
|
if (ensureLocalParentDirs) {
|
|
173
184
|
await fs.mkdir(path.dirname(resolvedLocalPath), { recursive: true });
|
|
@@ -215,8 +226,8 @@ export function expandSkillUrls(command: string, skills: readonly Skill[]): stri
|
|
|
215
226
|
}
|
|
216
227
|
|
|
217
228
|
/**
|
|
218
|
-
* Expand
|
|
219
|
-
*
|
|
229
|
+
* Expand filesystem-safe internal URLs in a bash command string. `agent://` and
|
|
230
|
+
* `artifact://` remain internal-reader-only and are rejected before resolution.
|
|
220
231
|
*/
|
|
221
232
|
export async function expandInternalUrls(command: string, options: InternalUrlExpansionOptions): Promise<string> {
|
|
222
233
|
if (!command.includes("://") && !command.includes("local:/")) return command;
|
package/src/tools/bash.ts
CHANGED
|
@@ -636,6 +636,7 @@ export class BashTool implements AgentTool<BashToolSchema, BashToolDetails> {
|
|
|
636
636
|
internalRouter: InternalUrlRouter.instance(),
|
|
637
637
|
localOptions: {
|
|
638
638
|
getArtifactsDir: this.session.getArtifactsDir,
|
|
639
|
+
isManagedDestination: this.session.isManagedSessionDestination,
|
|
639
640
|
getSessionId: this.session.getSessionId,
|
|
640
641
|
},
|
|
641
642
|
};
|
|
@@ -643,7 +644,6 @@ export class BashTool implements AgentTool<BashToolSchema, BashToolDetails> {
|
|
|
643
644
|
...internalUrlOptions,
|
|
644
645
|
ensureLocalParentDirs: this.session.bashRestrictionProfile !== "read-only",
|
|
645
646
|
});
|
|
646
|
-
const sessionFile = this.session.getSessionFile?.() ?? null;
|
|
647
647
|
const expandedEnv = env
|
|
648
648
|
? Object.fromEntries(
|
|
649
649
|
await Promise.all(
|
|
@@ -662,7 +662,7 @@ export class BashTool implements AgentTool<BashToolSchema, BashToolDetails> {
|
|
|
662
662
|
: undefined;
|
|
663
663
|
const resolvedEnv = {
|
|
664
664
|
...buildGjcRuntimeSessionEnv({
|
|
665
|
-
sessionFile,
|
|
665
|
+
sessionFile: null,
|
|
666
666
|
sessionId: this.session.getSessionId?.(),
|
|
667
667
|
cwd: this.session.cwd,
|
|
668
668
|
}),
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import * as fs from "node:fs";
|
|
2
2
|
import * as os from "node:os";
|
|
3
3
|
import * as path from "node:path";
|
|
4
|
-
import {
|
|
4
|
+
import { getPuppeteerDir } from "@gajae-code/utils/dirs";
|
|
5
|
+
import * as logger from "@gajae-code/utils/logger";
|
|
6
|
+
import { $which } from "@gajae-code/utils/which";
|
|
5
7
|
import * as browsers from "@puppeteer/browsers";
|
|
6
8
|
import type { Browser, CDPSession, Page, default as Puppeteer, Target } from "puppeteer-core";
|
|
7
9
|
import { PUPPETEER_REVISIONS } from "puppeteer-core/internal/revisions.js";
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Readability } from "@mozilla/readability";
|
|
2
2
|
import { parseHtmlLazy } from "../../utils/linkedom";
|
|
3
|
-
import { htmlToBasicMarkdown } from "../../web/scrapers/
|
|
3
|
+
import { htmlToBasicMarkdown } from "../../web/scrapers/html-to-markdown";
|
|
4
4
|
|
|
5
5
|
export type ReadableFormat = "text" | "markdown";
|
|
6
6
|
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import * as os from "node:os";
|
|
2
|
+
|
|
3
|
+
import { formatDimensionNote, type ResizedImage } from "../../utils/image-resize";
|
|
4
|
+
|
|
5
|
+
function shortenPath(filePath: string): string {
|
|
6
|
+
const home = os.homedir();
|
|
7
|
+
return home && filePath.startsWith(home) ? `~${filePath.slice(home.length)}` : filePath;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export function formatScreenshot(opts: {
|
|
11
|
+
saveFullRes: boolean;
|
|
12
|
+
savedMimeType: string;
|
|
13
|
+
savedByteLength: number;
|
|
14
|
+
dest: string;
|
|
15
|
+
resized: ResizedImage;
|
|
16
|
+
}): string[] {
|
|
17
|
+
const lines = ["Screenshot captured"];
|
|
18
|
+
if (opts.saveFullRes) {
|
|
19
|
+
lines.push(
|
|
20
|
+
`Saved: ${opts.savedMimeType} (${(opts.savedByteLength / 1024).toFixed(2)} KB) to ${shortenPath(opts.dest)}`,
|
|
21
|
+
);
|
|
22
|
+
lines.push(
|
|
23
|
+
`Model: ${opts.resized.mimeType} (${(opts.resized.buffer.length / 1024).toFixed(2)} KB, ${opts.resized.width}x${opts.resized.height})`,
|
|
24
|
+
);
|
|
25
|
+
} else {
|
|
26
|
+
lines.push(`Format: ${opts.resized.mimeType} (${(opts.resized.buffer.length / 1024).toFixed(2)} KB)`);
|
|
27
|
+
lines.push(`Dimensions: ${opts.resized.width}x${opts.resized.height}`);
|
|
28
|
+
}
|
|
29
|
+
const dimensionNote = formatDimensionNote(opts.resized);
|
|
30
|
+
if (dimensionNote) {
|
|
31
|
+
lines.push(dimensionNote);
|
|
32
|
+
}
|
|
33
|
+
return lines;
|
|
34
|
+
}
|
|
@@ -63,6 +63,7 @@ export type WorkerInitPayload =
|
|
|
63
63
|
export type ToolReply = { ok: true; value: unknown } | { ok: false; error: RunErrorPayload };
|
|
64
64
|
|
|
65
65
|
export type WorkerInbound =
|
|
66
|
+
| { type: "bootstrap"; version: 1; mode: "native-free" }
|
|
66
67
|
| { type: "init"; payload: WorkerInitPayload }
|
|
67
68
|
| { type: "run"; id: string; name: string; code: string; timeoutMs: number; session: SessionSnapshot }
|
|
68
69
|
| { type: "abort"; id: string }
|
|
@@ -91,6 +92,8 @@ export interface RunErrorPayload {
|
|
|
91
92
|
}
|
|
92
93
|
|
|
93
94
|
export type WorkerOutbound =
|
|
95
|
+
| { type: "bootstrap-ready"; version: 1; mode: "native-free" }
|
|
96
|
+
| { type: "bootstrap-failed"; error: string }
|
|
94
97
|
| { type: "ready"; info: ReadyInfo }
|
|
95
98
|
| { type: "init-failed"; error: RunErrorPayload }
|
|
96
99
|
| { type: "result"; id: string; ok: true; payload: RunResultOk }
|
|
@@ -19,7 +19,6 @@ import type {
|
|
|
19
19
|
RunResultOk,
|
|
20
20
|
SessionSnapshot,
|
|
21
21
|
Transferable,
|
|
22
|
-
Transport,
|
|
23
22
|
WorkerInbound,
|
|
24
23
|
WorkerInitPayload,
|
|
25
24
|
WorkerOutbound,
|
|
@@ -38,8 +37,8 @@ interface WorkerHandle {
|
|
|
38
37
|
send(msg: WorkerInbound, transferList?: Transferable[]): void;
|
|
39
38
|
onMessage(handler: (msg: WorkerOutbound) => void): () => void;
|
|
40
39
|
onError(handler: (error: Error) => void): () => void;
|
|
40
|
+
onClose(handler: () => void): () => void;
|
|
41
41
|
terminate(): Promise<void>;
|
|
42
|
-
readonly mode: "worker" | "inline";
|
|
43
42
|
}
|
|
44
43
|
|
|
45
44
|
export type DialogPolicy = "accept" | "dismiss";
|
|
@@ -118,6 +117,13 @@ let workerInitializerForTest:
|
|
|
118
117
|
| ((worker: WorkerHandle, payload: WorkerInitPayload, timeoutMs: number) => Promise<ReadyInfo>)
|
|
119
118
|
| undefined;
|
|
120
119
|
const GRACE_MS = 750;
|
|
120
|
+
const TAB_WORKER_MODE = "native-free";
|
|
121
|
+
|
|
122
|
+
function startupError(stage: string): ToolError {
|
|
123
|
+
return new ToolError(
|
|
124
|
+
`Tab worker startup failed (stage=${stage}, mode=${TAB_WORKER_MODE}, platform=${process.platform}).`,
|
|
125
|
+
);
|
|
126
|
+
}
|
|
121
127
|
|
|
122
128
|
interface DeadTabDescriptor {
|
|
123
129
|
tab: TabSession;
|
|
@@ -152,7 +158,7 @@ async function awaitWithinBudget(op: Promise<unknown>, remainingMs: number, labe
|
|
|
152
158
|
logger.debug("close teardown budget already spent; detaching step", { label });
|
|
153
159
|
return false;
|
|
154
160
|
}
|
|
155
|
-
let timer:
|
|
161
|
+
let timer: NodeJS.Timeout | undefined;
|
|
156
162
|
const timedOut = new Promise<boolean>(resolve => {
|
|
157
163
|
timer = setTimeout(() => resolve(false), remainingMs);
|
|
158
164
|
});
|
|
@@ -358,64 +364,69 @@ export async function acquireTab(
|
|
|
358
364
|
if (replacementHold) await releaseBrowser(browser, { kill: false });
|
|
359
365
|
throw error;
|
|
360
366
|
}
|
|
361
|
-
let worker
|
|
362
|
-
let info: ReadyInfo;
|
|
367
|
+
let worker: WorkerHandle;
|
|
363
368
|
try {
|
|
364
|
-
|
|
365
|
-
} catch
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
);
|
|
388
|
-
|
|
389
|
-
|
|
369
|
+
worker = await spawnAcquireWorker();
|
|
370
|
+
} catch {
|
|
371
|
+
if (replacementHold) await releaseBrowser(browser, { kill: false });
|
|
372
|
+
else if (browser.refCount === 0) await releaseBrowser(browser, { kill: false });
|
|
373
|
+
throw startupError("spawn");
|
|
374
|
+
}
|
|
375
|
+
let registeredTab: TabSession | undefined;
|
|
376
|
+
let startupFailure: Error | undefined;
|
|
377
|
+
let browserHeld = false;
|
|
378
|
+
const observeFailure = (error: Error): void => {
|
|
379
|
+
if (registeredTab) markTabDead(registeredTab, error);
|
|
380
|
+
else startupFailure ??= error;
|
|
381
|
+
};
|
|
382
|
+
const unlistenLifetimeMessages = worker.onMessage(msg => {
|
|
383
|
+
if (msg.type === "closed") observeFailure(startupError("protocol-closed"));
|
|
384
|
+
});
|
|
385
|
+
const unlistenLifetimeErrors = worker.onError(() => observeFailure(startupError("error")));
|
|
386
|
+
const unlistenLifetimeClose = worker.onClose(() => observeFailure(startupError("physical-close")));
|
|
387
|
+
try {
|
|
388
|
+
const info = await initializeAcquireWorker(worker, initPayload, opts.timeoutMs + GRACE_MS);
|
|
389
|
+
afterWorkerInitializationForTest?.(name);
|
|
390
|
+
if (startupFailure) throw startupFailure;
|
|
391
|
+
if (opts.requireVacantName && tabs.has(name))
|
|
392
|
+
throw new ToolError(`Tab ${JSON.stringify(name)} was replaced during recovery.`);
|
|
393
|
+
|
|
394
|
+
holdBrowser(browser);
|
|
395
|
+
browserHeld = true;
|
|
396
|
+
if (replacementHold) {
|
|
397
|
+
await releaseBrowser(browser, { kill: false });
|
|
398
|
+
replacementHold = false;
|
|
390
399
|
}
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
400
|
+
if (startupFailure) throw startupFailure;
|
|
401
|
+
|
|
402
|
+
const tab: TabSession = {
|
|
403
|
+
name,
|
|
404
|
+
browser,
|
|
405
|
+
targetId: info.targetId,
|
|
406
|
+
worker,
|
|
407
|
+
state: "alive",
|
|
408
|
+
info,
|
|
409
|
+
pending: new Map(),
|
|
410
|
+
dialogPolicy: opts.dialogs,
|
|
411
|
+
kindTag: browser.kind.kind,
|
|
412
|
+
ownerId: opts.ownerId,
|
|
413
|
+
lastUsedAt: Date.now(),
|
|
414
|
+
recoveryOpts: freezeRecoveryOptions(opts),
|
|
415
|
+
};
|
|
416
|
+
registeredTab = tab;
|
|
417
|
+
worker.onMessage(msg => handleTabMessage(tab, msg));
|
|
418
|
+
tabs.set(name, tab);
|
|
419
|
+
return { tab, created: true };
|
|
420
|
+
} catch (error) {
|
|
421
|
+
unlistenLifetimeMessages();
|
|
422
|
+
unlistenLifetimeErrors();
|
|
423
|
+
unlistenLifetimeClose();
|
|
395
424
|
await worker.terminate().catch(() => undefined);
|
|
425
|
+
if (browserHeld) await releaseBrowser(browser, { kill: false });
|
|
396
426
|
if (replacementHold) await releaseBrowser(browser, { kill: false });
|
|
397
|
-
|
|
427
|
+
else if (!browserHeld && browser.refCount === 0) await releaseBrowser(browser, { kill: false });
|
|
428
|
+
throw error;
|
|
398
429
|
}
|
|
399
|
-
holdBrowser(browser);
|
|
400
|
-
if (replacementHold) await releaseBrowser(browser, { kill: false });
|
|
401
|
-
const tab: TabSession = {
|
|
402
|
-
name,
|
|
403
|
-
browser,
|
|
404
|
-
targetId: info.targetId,
|
|
405
|
-
worker,
|
|
406
|
-
state: "alive",
|
|
407
|
-
info,
|
|
408
|
-
pending: new Map(),
|
|
409
|
-
dialogPolicy: opts.dialogs,
|
|
410
|
-
kindTag: browser.kind.kind,
|
|
411
|
-
ownerId: opts.ownerId,
|
|
412
|
-
lastUsedAt: Date.now(),
|
|
413
|
-
recoveryOpts: freezeRecoveryOptions(opts),
|
|
414
|
-
};
|
|
415
|
-
worker.onMessage(msg => handleTabMessage(tab, msg));
|
|
416
|
-
worker.onError(error => markTabDead(tab, error));
|
|
417
|
-
tabs.set(name, tab);
|
|
418
|
-
return { tab, created: true };
|
|
419
430
|
}
|
|
420
431
|
|
|
421
432
|
export async function runInTab(name: string, opts: RunInTabOptions): Promise<RunResultOk> {
|
|
@@ -871,22 +882,14 @@ async function raceWithTimeout<T>(
|
|
|
871
882
|
}
|
|
872
883
|
|
|
873
884
|
async function spawnTabWorker(): Promise<WorkerHandle> {
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
return wrapBunWorker(worker);
|
|
879
|
-
} catch (err) {
|
|
880
|
-
logger.warn("Bun Worker spawn failed; using inline tab worker (no sync-loop guard)", {
|
|
881
|
-
error: err instanceof Error ? err.message : String(err),
|
|
882
|
-
});
|
|
883
|
-
return spawnInlineWorker();
|
|
884
|
-
}
|
|
885
|
+
const worker = isCompiledBinary()
|
|
886
|
+
? new Worker("./packages/coding-agent/src/tools/browser/tab-worker-entry.ts", { type: "module" })
|
|
887
|
+
: new Worker(new URL("./tab-worker-entry.ts", import.meta.url).href, { type: "module" });
|
|
888
|
+
return wrapBunWorker(worker);
|
|
885
889
|
}
|
|
886
890
|
|
|
887
891
|
function wrapBunWorker(worker: Worker): WorkerHandle {
|
|
888
892
|
return {
|
|
889
|
-
mode: "worker",
|
|
890
893
|
send(msg, transferList) {
|
|
891
894
|
worker.postMessage(msg, { transfer: transferList ?? [] });
|
|
892
895
|
},
|
|
@@ -896,9 +899,8 @@ function wrapBunWorker(worker: Worker): WorkerHandle {
|
|
|
896
899
|
return () => worker.removeEventListener("message", wrap);
|
|
897
900
|
},
|
|
898
901
|
onError(handler) {
|
|
899
|
-
const onError = (
|
|
900
|
-
const onMessageError = (
|
|
901
|
-
handler(new ToolError(`Tab worker message error: ${String(event.data)}`));
|
|
902
|
+
const onError = (): void => handler(startupError("error"));
|
|
903
|
+
const onMessageError = (): void => handler(startupError("messageerror"));
|
|
902
904
|
worker.addEventListener("error", onError);
|
|
903
905
|
worker.addEventListener("messageerror", onMessageError);
|
|
904
906
|
return () => {
|
|
@@ -906,69 +908,76 @@ function wrapBunWorker(worker: Worker): WorkerHandle {
|
|
|
906
908
|
worker.removeEventListener("messageerror", onMessageError);
|
|
907
909
|
};
|
|
908
910
|
},
|
|
911
|
+
onClose(handler) {
|
|
912
|
+
const onClose = (): void => handler();
|
|
913
|
+
worker.addEventListener("close", onClose);
|
|
914
|
+
return () => worker.removeEventListener("close", onClose);
|
|
915
|
+
},
|
|
909
916
|
async terminate() {
|
|
910
917
|
worker.terminate();
|
|
911
918
|
},
|
|
912
919
|
};
|
|
913
920
|
}
|
|
914
921
|
|
|
915
|
-
/**
|
|
916
|
-
* Inline fallback for environments where Bun cannot compile or spawn the worker
|
|
917
|
-
* entry. This preserves normal browser behavior but cannot interrupt synchronous
|
|
918
|
-
* infinite loops because user code runs on the main thread.
|
|
919
|
-
*/
|
|
920
|
-
async function spawnInlineWorker(): Promise<WorkerHandle> {
|
|
921
|
-
const hostListeners = new Set<(message: WorkerOutbound) => void>();
|
|
922
|
-
const workerListeners = new Set<(message: WorkerInbound) => void>();
|
|
923
|
-
const workerTransport: Transport = {
|
|
924
|
-
send: msg =>
|
|
925
|
-
queueMicrotask(() => {
|
|
926
|
-
for (const listener of hostListeners) listener(msg as WorkerOutbound);
|
|
927
|
-
}),
|
|
928
|
-
onMessage: handler => {
|
|
929
|
-
const typed = handler as (message: WorkerInbound) => void;
|
|
930
|
-
workerListeners.add(typed);
|
|
931
|
-
return () => workerListeners.delete(typed);
|
|
932
|
-
},
|
|
933
|
-
close: () => {},
|
|
934
|
-
};
|
|
935
|
-
const { WorkerCore } = await import("./tab-worker");
|
|
936
|
-
new WorkerCore(workerTransport);
|
|
937
|
-
return {
|
|
938
|
-
mode: "inline",
|
|
939
|
-
send: msg =>
|
|
940
|
-
queueMicrotask(() => {
|
|
941
|
-
for (const listener of workerListeners) listener(msg);
|
|
942
|
-
}),
|
|
943
|
-
onMessage: handler => {
|
|
944
|
-
hostListeners.add(handler);
|
|
945
|
-
return () => hostListeners.delete(handler);
|
|
946
|
-
},
|
|
947
|
-
onError: () => () => {},
|
|
948
|
-
async terminate() {},
|
|
949
|
-
};
|
|
950
|
-
}
|
|
951
|
-
|
|
952
922
|
async function initializeTabWorker(
|
|
953
923
|
worker: WorkerHandle,
|
|
954
924
|
payload: WorkerInitPayload,
|
|
955
925
|
timeoutMs: number,
|
|
956
926
|
): Promise<ReadyInfo> {
|
|
957
|
-
|
|
927
|
+
type StartupPhase = "await-bootstrap" | "bootstrap-confirmed" | "await-init";
|
|
928
|
+
let phase: StartupPhase = "await-bootstrap";
|
|
929
|
+
const { promise: bootstrap, resolve: bootstrapReady, reject: rejectBootstrap } = Promise.withResolvers<void>();
|
|
930
|
+
const { promise: initialized, resolve: ready, reject: rejectInitialized } = Promise.withResolvers<ReadyInfo>();
|
|
931
|
+
void initialized.catch(() => undefined);
|
|
932
|
+
const fail = (stage: string): void => {
|
|
933
|
+
const error = startupError(stage);
|
|
934
|
+
rejectBootstrap(error);
|
|
935
|
+
rejectInitialized(error);
|
|
936
|
+
};
|
|
958
937
|
const unlisten = worker.onMessage(msg => {
|
|
959
|
-
if (msg.type === "
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
938
|
+
if (msg.type === "log") {
|
|
939
|
+
logWorkerMessage(msg);
|
|
940
|
+
return;
|
|
941
|
+
}
|
|
942
|
+
if (phase === "await-bootstrap") {
|
|
943
|
+
if (msg.type === "bootstrap-ready" && msg.version === 1 && msg.mode === TAB_WORKER_MODE) {
|
|
944
|
+
phase = "bootstrap-confirmed";
|
|
945
|
+
bootstrapReady();
|
|
946
|
+
} else if (msg.type === "bootstrap-failed") {
|
|
947
|
+
fail("bootstrap");
|
|
948
|
+
} else if (msg.type === "closed") {
|
|
949
|
+
fail("protocol-closed");
|
|
950
|
+
} else {
|
|
951
|
+
fail("protocol-phase");
|
|
952
|
+
}
|
|
953
|
+
return;
|
|
954
|
+
}
|
|
955
|
+
if (phase !== "await-init") {
|
|
956
|
+
fail("protocol-phase");
|
|
957
|
+
return;
|
|
958
|
+
}
|
|
959
|
+
if (msg.type === "ready") {
|
|
960
|
+
ready(msg.info);
|
|
961
|
+
} else if (msg.type === "init-failed") {
|
|
962
|
+
fail("init");
|
|
963
|
+
} else if (msg.type === "closed") {
|
|
964
|
+
fail("protocol-closed");
|
|
965
|
+
} else {
|
|
966
|
+
fail("protocol-phase");
|
|
967
|
+
}
|
|
965
968
|
});
|
|
969
|
+
const unlistenError = worker.onError(() => fail("error"));
|
|
970
|
+
const unlistenClose = worker.onClose(() => fail("physical-close"));
|
|
966
971
|
try {
|
|
972
|
+
worker.send({ type: "bootstrap", version: 1, mode: TAB_WORKER_MODE });
|
|
973
|
+
await raceWithTimeout(bootstrap, timeoutMs, startupError("bootstrap-timeout").message);
|
|
974
|
+
phase = "await-init";
|
|
967
975
|
worker.send({ type: "init", payload });
|
|
968
|
-
return await raceWithTimeout(
|
|
976
|
+
return await raceWithTimeout(initialized, timeoutMs, startupError("init-timeout").message);
|
|
969
977
|
} finally {
|
|
970
978
|
unlisten();
|
|
971
979
|
unlistenError();
|
|
980
|
+
unlistenClose();
|
|
972
981
|
}
|
|
973
982
|
}
|
|
974
983
|
|
|
@@ -979,9 +988,3 @@ export function initializeTabWorkerForTest(
|
|
|
979
988
|
): Promise<ReadyInfo> {
|
|
980
989
|
return initializeTabWorker(worker, payload, timeoutMs);
|
|
981
990
|
}
|
|
982
|
-
|
|
983
|
-
function errorFromWorkerEvent(event: ErrorEvent): Error {
|
|
984
|
-
if (event.error instanceof Error) return event.error;
|
|
985
|
-
if (event.message) return new Error(event.message);
|
|
986
|
-
return new Error("Unknown tab worker error");
|
|
987
|
-
}
|