@gajae-code/coding-agent 0.12.21 → 0.13.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +67 -2
- package/dist/types/cli/args.d.ts +5 -2
- package/dist/types/cli/root-flags.d.ts +246 -0
- package/dist/types/cli.d.ts +11 -19
- package/dist/types/commands/daemon.d.ts +0 -30
- package/dist/types/commands/launch.d.ts +11 -19
- package/dist/types/commands/quick-lane.d.ts +32 -0
- package/dist/types/commands/sdk.d.ts +21 -15
- package/dist/types/config/file-lock-gc.d.ts +2 -0
- package/dist/types/config/model-equivalence.d.ts +20 -0
- package/dist/types/config/model-profile-activation.d.ts +62 -17
- package/dist/types/config/model-profiles.d.ts +10 -0
- package/dist/types/config/model-registry.d.ts +68 -1
- package/dist/types/config/model-resolver.d.ts +15 -4
- package/dist/types/config/models-config-schema.d.ts +7 -2
- package/dist/types/config/provider-selection-policy.d.ts +62 -0
- package/dist/types/config/settings-schema.d.ts +36 -78
- package/dist/types/config/settings.d.ts +2 -0
- package/dist/types/edit/renderer.d.ts +5 -2
- package/dist/types/edit/streaming.d.ts +13 -0
- package/dist/types/extensibility/custom-tools/types.d.ts +2 -0
- package/dist/types/extensibility/extensions/compact-handler.d.ts +2 -1
- package/dist/types/extensibility/extensions/types.d.ts +3 -0
- package/dist/types/gjc-runtime/gc-runtime.d.ts +19 -5
- package/dist/types/internal-urls/local-protocol.d.ts +1 -0
- package/dist/types/modes/components/model-selector.d.ts +0 -2
- package/dist/types/modes/components/provider-order-context.d.ts +45 -0
- package/dist/types/modes/components/provider-order-editor.d.ts +24 -0
- package/dist/types/modes/components/settings-defs.d.ts +5 -1
- package/dist/types/modes/components/settings-selector.d.ts +2 -0
- package/dist/types/quick-lane/classify.d.ts +40 -0
- package/dist/types/runtime/stt-settings-setup.d.ts +18 -0
- package/dist/types/sdk/broker/broker.d.ts +56 -2
- package/dist/types/sdk/broker/operation-receipt-ledger.d.ts +25 -0
- package/dist/types/sdk/broker/session-index.d.ts +83 -2
- package/dist/types/sdk/broker/state-version.d.ts +1 -1
- package/dist/types/sdk/bus/notification-service.d.ts +6 -0
- package/dist/types/sdk/bus/prompt-reconciliation.d.ts +8 -1
- package/dist/types/sdk/bus/telegram-daemon-contract.d.ts +4 -1
- package/dist/types/sdk/bus/telegram-daemon.d.ts +26 -0
- package/dist/types/sdk/cli/rows.d.ts +98 -0
- package/dist/types/sdk/cli/session-cli.d.ts +35 -3
- package/dist/types/sdk/elevation/allowlist.d.ts +20 -0
- package/dist/types/sdk/elevation/audit.d.ts +93 -0
- package/dist/types/sdk/elevation/capability.d.ts +3 -0
- package/dist/types/sdk/elevation/digest.d.ts +72 -0
- package/dist/types/sdk/elevation/dispatch-receipt.d.ts +85 -0
- package/dist/types/sdk/elevation/grant-ledger.d.ts +163 -0
- package/dist/types/sdk/elevation/owner.d.ts +48 -0
- package/dist/types/sdk/guides/cache.d.ts +98 -0
- package/dist/types/sdk/guides/catalog.d.ts +156 -0
- package/dist/types/sdk/guides/cli.d.ts +39 -0
- package/dist/types/sdk/guides/manifest.d.ts +66 -0
- package/dist/types/sdk/guides/verify.d.ts +66 -0
- package/dist/types/sdk/host/control/dispatch.d.ts +8 -0
- package/dist/types/sdk/host/control/index.d.ts +1 -1
- package/dist/types/sdk/host/control/operations.d.ts +3 -2
- package/dist/types/sdk/host/query/cursor.d.ts +10 -0
- package/dist/types/sdk/host/query/handlers.d.ts +21 -0
- package/dist/types/sdk/host/session-runtime.d.ts +9 -0
- package/dist/types/sdk/mcp/server.d.ts +2 -2
- package/dist/types/sdk/prompt-status.d.ts +11 -0
- package/dist/types/sdk/session.d.ts +4 -0
- package/dist/types/sdk/transport/index.d.ts +1 -0
- package/dist/types/sdk/transport/relay.d.ts +1 -0
- package/dist/types/sdk/transport/serve-cli.d.ts +20 -1
- package/dist/types/session/agent-session.d.ts +32 -18
- package/dist/types/session/blob-store.d.ts +13 -7
- package/dist/types/session/fallback-chain-controller.d.ts +15 -0
- package/dist/types/session/internal/managed-session-storage.d.ts +25 -1
- package/dist/types/session/internal/session-memory-sidecar.d.ts +5 -5
- package/dist/types/session/session-manager.d.ts +139 -10
- package/dist/types/session/session-storage.d.ts +48 -0
- package/dist/types/setup/provider-onboarding.d.ts +7 -1
- package/dist/types/slash-commands/builtin-registry.d.ts +14 -0
- package/dist/types/stt/setup.d.ts +2 -0
- package/dist/types/task/executor.d.ts +10 -2
- package/dist/types/tools/browser/runtime-diagnostics.d.ts +65 -0
- package/dist/types/tools/browser/tab-protocol.d.ts +4 -0
- package/dist/types/tools/browser/tab-supervisor.d.ts +2 -0
- package/dist/types/tools/browser/tab-worker.d.ts +2 -0
- package/dist/types/tools/browser.d.ts +2 -0
- package/dist/types/tools/image-gen.d.ts +14 -18
- package/dist/types/tools/index.d.ts +2 -0
- package/dist/types/tools/resource-gc.d.ts +2 -0
- package/dist/types/utils/pasted-image-path.d.ts +6 -0
- package/package.json +9 -9
- package/scripts/generate-sdk-operation-inventory.ts +8 -0
- package/scripts/run-test-manifest.ts +61 -21
- package/scripts/verify-gjc-sdk-canonicalization.ts +9 -4
- package/src/cli/args.ts +62 -9
- package/src/cli/completion-cli.ts +1 -0
- package/src/cli/fast-help.ts +1 -0
- package/src/cli/root-flags.ts +100 -0
- package/src/cli/setup-cli.ts +6 -2
- package/src/cli.ts +42 -65
- package/src/commands/acp.ts +1 -1
- package/src/commands/daemon.ts +2 -31
- package/src/commands/gc.ts +1 -1
- package/src/commands/launch.ts +8 -129
- package/src/commands/quick-lane.ts +51 -0
- package/src/commands/sdk.ts +200 -26
- package/src/commands/setup.ts +4 -2
- package/src/config/file-lock-gc.ts +4 -1
- package/src/config/model-equivalence.ts +76 -7
- package/src/config/model-profile-activation.ts +1115 -265
- package/src/config/model-profiles.ts +134 -2
- package/src/config/model-registry.ts +352 -50
- package/src/config/model-resolver.ts +289 -38
- package/src/config/models-config-schema.ts +11 -4
- package/src/config/provider-selection-policy.ts +146 -0
- package/src/config/settings-schema.ts +50 -77
- package/src/config/settings.ts +40 -0
- package/src/coordinator-mcp/server.ts +32 -2
- package/src/edit/renderer.ts +175 -223
- package/src/edit/streaming.ts +108 -1
- package/src/export/html/index.ts +57 -7
- package/src/extensibility/custom-tools/types.ts +2 -0
- package/src/extensibility/extensions/compact-handler.ts +3 -2
- package/src/extensibility/extensions/runner.ts +6 -0
- package/src/extensibility/extensions/types.ts +3 -0
- package/src/gjc-runtime/gc-runtime.ts +379 -20
- package/src/gjc-runtime/launch-worktree.ts +5 -1
- package/src/gjc-runtime/team-runtime.ts +59 -2
- package/src/gjc-runtime/tmux-owner-isolation.ts +19 -0
- package/src/gjc-runtime/ultragoal-receipt-freshness.ts +16 -1
- package/src/internal-urls/docs-index.generated.ts +17 -13
- package/src/internal-urls/local-protocol.ts +1 -0
- package/src/main.ts +40 -3
- package/src/memories/index.ts +4 -2
- package/src/modes/acp/acp-agent.ts +235 -91
- package/src/modes/components/model-selector.ts +206 -42
- package/src/modes/components/provider-order-context.ts +197 -0
- package/src/modes/components/provider-order-editor.ts +441 -0
- package/src/modes/components/settings-defs.ts +21 -1
- package/src/modes/components/settings-selector.ts +45 -2
- package/src/modes/components/tool-execution.ts +47 -15
- package/src/modes/controllers/command-controller.ts +6 -3
- package/src/modes/controllers/extension-ui-controller.ts +2 -0
- package/src/modes/controllers/input-controller.ts +73 -2
- package/src/modes/controllers/selector-controller.ts +260 -153
- package/src/modes/prompt-suggestion-controller.ts +1 -1
- package/src/modes/runtime-init.ts +1 -0
- package/src/modes/utils/ui-helpers.ts +18 -0
- package/src/prompts/tools/browser.md +1 -0
- package/src/quick-lane/classify.ts +257 -0
- package/src/rlm/index.ts +1 -1
- package/src/runtime/stt-settings-setup.ts +57 -0
- package/src/sdk/acp/adapter.ts +2 -1
- package/src/sdk/broker/broker.ts +780 -12
- package/src/sdk/broker/lifecycle.ts +70 -29
- package/src/sdk/broker/operation-receipt-ledger.ts +144 -0
- package/src/sdk/broker/session-index.ts +463 -48
- package/src/sdk/broker/state-version.ts +13 -9
- package/src/sdk/broker/transport.ts +14 -1
- package/src/sdk/bus/index.ts +34 -13
- package/src/sdk/bus/kind-aware-reconciliation.ts +21 -2
- package/src/sdk/bus/notification-service.ts +47 -1
- package/src/sdk/bus/prompt-reconciliation.ts +22 -3
- package/src/sdk/bus/telegram-daemon-contract.ts +4 -1
- package/src/sdk/bus/telegram-daemon.ts +54 -3
- package/src/sdk/cli/rows.ts +247 -0
- package/src/sdk/cli/session-cli.ts +930 -79
- package/src/sdk/elevation/allowlist.ts +54 -0
- package/src/sdk/elevation/audit.ts +208 -0
- package/src/sdk/elevation/capability.ts +41 -0
- package/src/sdk/elevation/digest.ts +129 -0
- package/src/sdk/elevation/dispatch-receipt.ts +184 -0
- package/src/sdk/elevation/grant-ledger.ts +1142 -0
- package/src/sdk/elevation/owner.ts +109 -0
- package/src/sdk/guides/cache.ts +363 -0
- package/src/sdk/guides/catalog.ts +554 -0
- package/src/sdk/guides/cli.ts +235 -0
- package/src/sdk/guides/manifest.ts +152 -0
- package/src/sdk/guides/verify.ts +178 -0
- package/src/sdk/host/control/dispatch.ts +49 -1
- package/src/sdk/host/control/index.ts +1 -0
- package/src/sdk/host/control/operations.ts +8 -1
- package/src/sdk/host/query/cursor.ts +47 -3
- package/src/sdk/host/query/handlers.ts +155 -2
- package/src/sdk/host/sdk-surface-parity.test.ts +1 -0
- package/src/sdk/host/session-runtime.test.ts +391 -1
- package/src/sdk/host/session-runtime.ts +308 -73
- package/src/sdk/host/surface-policy.ts +3 -0
- package/src/sdk/host/websocket-transport.ts +7 -1
- package/src/sdk/mcp/server.ts +88 -15
- package/src/sdk/prompt-status.ts +8 -0
- package/src/sdk/protocol/operation-inventory.generated.json +62 -0
- package/src/sdk/protocol/operation-registry.ts +3 -2
- package/src/sdk/session.ts +87 -39
- package/src/sdk/transport/index.ts +1 -0
- package/src/sdk/transport/relay.ts +3 -0
- package/src/sdk/transport/serve-cli.ts +111 -24
- package/src/secrets/obfuscator.ts +92 -12
- package/src/session/agent-session.ts +322 -93
- package/src/session/blob-store.ts +60 -8
- package/src/session/fallback-chain-controller.ts +35 -0
- package/src/session/internal/managed-session-scope.ts +25 -3
- package/src/session/internal/managed-session-storage.ts +574 -73
- package/src/session/internal/session-memory-sidecar.ts +19 -29
- package/src/session/session-manager.ts +2166 -743
- package/src/session/session-storage.ts +715 -48
- package/src/setup/provider-onboarding.ts +19 -3
- package/src/setup/provider-presets.json +3 -1
- package/src/slash-commands/builtin-registry.ts +85 -3
- package/src/stt/downloader.ts +2 -3
- package/src/stt/setup.ts +29 -4
- package/src/task/executor.ts +22 -18
- package/src/task/index.ts +24 -1
- package/src/tools/archive-reader.ts +11 -2
- package/src/tools/browser/runtime-diagnostics.ts +249 -0
- package/src/tools/browser/tab-protocol.ts +4 -0
- package/src/tools/browser/tab-supervisor.ts +5 -0
- package/src/tools/browser/tab-worker.ts +36 -1
- package/src/tools/browser.ts +7 -0
- package/src/tools/image-gen.ts +110 -166
- package/src/tools/index.ts +2 -0
- package/src/tools/read.ts +1 -1
- package/src/tools/resource-gc.ts +6 -1
- package/src/tools/tool-catalog.generated.ts +5 -1
- package/src/utils/pasted-image-path.ts +93 -0
- package/src/web/search/index.ts +3 -3
|
@@ -0,0 +1,249 @@
|
|
|
1
|
+
import { createHash } from "node:crypto";
|
|
2
|
+
import type { CDPSession, Page } from "puppeteer-core";
|
|
3
|
+
|
|
4
|
+
const MAX_RUNTIME_DIAGNOSTICS = 20;
|
|
5
|
+
|
|
6
|
+
/** Hard cap on the `url` field (chars). Truncation is visible: the value ends in `…`. */
|
|
7
|
+
const MAX_RUNTIME_DIAGNOSTIC_URL_CHARS = 256;
|
|
8
|
+
|
|
9
|
+
/** Hard cap on the serialized diagnostics block (UTF-8 bytes) emitted into tool results. */
|
|
10
|
+
const MAX_RUNTIME_DIAGNOSTICS_BLOCK_BYTES = 4 * 1024;
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Fixed allowlist of built-in error class names. Page-controlled class names (anything
|
|
14
|
+
* defined by page scripts, e.g. `CustomerAlice123`) are never echoed: a diagnostic's
|
|
15
|
+
* `class` field is emitted only when it matches one of these fixed names.
|
|
16
|
+
*/
|
|
17
|
+
const ERROR_CLASS_ALLOWLIST = new Set([
|
|
18
|
+
"Error",
|
|
19
|
+
"EvalError",
|
|
20
|
+
"RangeError",
|
|
21
|
+
"ReferenceError",
|
|
22
|
+
"SyntaxError",
|
|
23
|
+
"TypeError",
|
|
24
|
+
"URIError",
|
|
25
|
+
"AggregateError",
|
|
26
|
+
"InternalError",
|
|
27
|
+
"DOMException",
|
|
28
|
+
"AbortError",
|
|
29
|
+
"CompileError",
|
|
30
|
+
"DataError",
|
|
31
|
+
"EncodingError",
|
|
32
|
+
"HierarchyRequestError",
|
|
33
|
+
"IndexSizeError",
|
|
34
|
+
"InvalidAccessError",
|
|
35
|
+
"InvalidCharacterError",
|
|
36
|
+
"InvalidModificationError",
|
|
37
|
+
"InvalidNodeTypeError",
|
|
38
|
+
"InvalidStateError",
|
|
39
|
+
"LinkError",
|
|
40
|
+
"NamespaceError",
|
|
41
|
+
"NetworkError",
|
|
42
|
+
"NoModificationAllowedError",
|
|
43
|
+
"NotFoundError",
|
|
44
|
+
"NotReadableError",
|
|
45
|
+
"NotSupportedError",
|
|
46
|
+
"OperationError",
|
|
47
|
+
"QuotaExceededError",
|
|
48
|
+
"ReadOnlyError",
|
|
49
|
+
"RuntimeError",
|
|
50
|
+
"SecurityError",
|
|
51
|
+
"TimeoutError",
|
|
52
|
+
"TransactionInactiveError",
|
|
53
|
+
"VersionError",
|
|
54
|
+
"WrongDocumentError",
|
|
55
|
+
]);
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Fixed, page-uncontrollable script-URL literals that V8 reports in stack frames and
|
|
59
|
+
* exception details. These are safe to surface verbatim; anything else that is not a
|
|
60
|
+
* parseable URL is emitted as an irreversible hash so a raw path/token cannot leak.
|
|
61
|
+
*/
|
|
62
|
+
const SAFE_NON_URL_LITERALS = new Set([
|
|
63
|
+
"",
|
|
64
|
+
"eval",
|
|
65
|
+
"anonymous",
|
|
66
|
+
"script",
|
|
67
|
+
"[native code]",
|
|
68
|
+
"__puppeteer_evaluation_script__",
|
|
69
|
+
]);
|
|
70
|
+
|
|
71
|
+
/** `about:` paths are page-navigable, so keep only browser-fixed safe names. */
|
|
72
|
+
const SAFE_ABOUT_PATH = /^[A-Za-z0-9._-]{1,32}$/;
|
|
73
|
+
|
|
74
|
+
export interface BrowserRuntimeDiagnostic {
|
|
75
|
+
kind: "pageerror" | "console-error";
|
|
76
|
+
at: string;
|
|
77
|
+
url: string;
|
|
78
|
+
line?: number;
|
|
79
|
+
column?: number;
|
|
80
|
+
class?: string;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
interface ExceptionThrownEvent {
|
|
84
|
+
exceptionDetails?: {
|
|
85
|
+
url?: string;
|
|
86
|
+
lineNumber?: number;
|
|
87
|
+
columnNumber?: number;
|
|
88
|
+
exception?: { className?: string };
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
interface ConsoleCalledEvent {
|
|
93
|
+
type?: string;
|
|
94
|
+
stackTrace?: { callFrames?: Array<{ url?: string; lineNumber?: number; columnNumber?: number }> };
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
function sha256Hex(value: string): string {
|
|
98
|
+
return createHash("sha256").update(value).digest("hex");
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* Reduce a runtime-event URL to a persisted-safe location.
|
|
103
|
+
*
|
|
104
|
+
* http(s) URLs are emitted origin-only: path segments routinely carry tokens (signed
|
|
105
|
+
* URLs, invite/reset links, per-tenant identifiers), and diagnostics end up in tool
|
|
106
|
+
* results that are persisted and shareable. Non-parseable values are never echoed
|
|
107
|
+
* verbatim — known engine literals pass through, everything else becomes an
|
|
108
|
+
* irreversible bounded hash so entries stay correlatable without leaking.
|
|
109
|
+
*/
|
|
110
|
+
export function maskBrowserRuntimeUrl(value: string): string {
|
|
111
|
+
let url: URL | undefined;
|
|
112
|
+
try {
|
|
113
|
+
url = new URL(value);
|
|
114
|
+
} catch {
|
|
115
|
+
url = undefined;
|
|
116
|
+
}
|
|
117
|
+
if (url) {
|
|
118
|
+
if (url.protocol === "http:" || url.protocol === "https:") {
|
|
119
|
+
return boundUrlField(url.origin);
|
|
120
|
+
}
|
|
121
|
+
if (url.protocol === "about:") {
|
|
122
|
+
const path = url.pathname && SAFE_ABOUT_PATH.test(url.pathname) ? `about:${url.pathname}` : "about:…";
|
|
123
|
+
return boundUrlField(path);
|
|
124
|
+
}
|
|
125
|
+
return boundUrlField(`${url.protocol}…`);
|
|
126
|
+
}
|
|
127
|
+
if (SAFE_NON_URL_LITERALS.has(value)) return value;
|
|
128
|
+
return `[non-url:${sha256Hex(value).slice(0, 8)}]`;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
function finite(value: unknown): number | undefined {
|
|
132
|
+
return typeof value === "number" && Number.isFinite(value) ? value : undefined;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
export function pageErrorDiagnostic(event: ExceptionThrownEvent, fallbackUrl: string): BrowserRuntimeDiagnostic {
|
|
136
|
+
const details = event.exceptionDetails;
|
|
137
|
+
const className = details?.exception?.className;
|
|
138
|
+
return {
|
|
139
|
+
kind: "pageerror",
|
|
140
|
+
at: new Date().toISOString(),
|
|
141
|
+
url: maskBrowserRuntimeUrl(details?.url || fallbackUrl),
|
|
142
|
+
...(finite(details?.lineNumber) === undefined ? {} : { line: details?.lineNumber }),
|
|
143
|
+
...(finite(details?.columnNumber) === undefined ? {} : { column: details?.columnNumber }),
|
|
144
|
+
...(typeof className === "string" && ERROR_CLASS_ALLOWLIST.has(className) ? { class: className } : {}),
|
|
145
|
+
};
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
export function consoleErrorDiagnostic(
|
|
149
|
+
event: ConsoleCalledEvent,
|
|
150
|
+
fallbackUrl: string,
|
|
151
|
+
): BrowserRuntimeDiagnostic | undefined {
|
|
152
|
+
if (event.type !== "error") return undefined;
|
|
153
|
+
const frame = event.stackTrace?.callFrames?.[0];
|
|
154
|
+
return {
|
|
155
|
+
kind: "console-error",
|
|
156
|
+
at: new Date().toISOString(),
|
|
157
|
+
url: maskBrowserRuntimeUrl(frame?.url || fallbackUrl),
|
|
158
|
+
...(finite(frame?.lineNumber) === undefined ? {} : { line: frame?.lineNumber }),
|
|
159
|
+
...(finite(frame?.columnNumber) === undefined ? {} : { column: frame?.columnNumber }),
|
|
160
|
+
};
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
function boundUrlField(url: string): string {
|
|
164
|
+
if (url.length <= MAX_RUNTIME_DIAGNOSTIC_URL_CHARS) return url;
|
|
165
|
+
return `${url.slice(0, MAX_RUNTIME_DIAGNOSTIC_URL_CHARS - 1)}…`;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
/**
|
|
169
|
+
* Serialize a drained mailbox into the tool-result text block.
|
|
170
|
+
*
|
|
171
|
+
* - Per-field: `url` is capped with a visible `…` marker; every other field is
|
|
172
|
+
* structurally bounded (ISO timestamp, finite integers, allowlisted class).
|
|
173
|
+
* - Total: the serialized block is capped at `MAX_RUNTIME_DIAGNOSTICS_BLOCK_BYTES`.
|
|
174
|
+
* When the budget is tight the oldest entries are shed (newest survive) and the
|
|
175
|
+
* block carries an explicit `runtimeDiagnosticsTruncated: true` — truncation is
|
|
176
|
+
* never silent.
|
|
177
|
+
*/
|
|
178
|
+
export function serializeRuntimeDiagnostics(drained: {
|
|
179
|
+
runtimeDiagnostics: BrowserRuntimeDiagnostic[];
|
|
180
|
+
runtimeDiagnosticsDropped: number;
|
|
181
|
+
}): string {
|
|
182
|
+
const { runtimeDiagnostics, runtimeDiagnosticsDropped } = drained;
|
|
183
|
+
const bounded = runtimeDiagnostics.map(entry => ({ ...entry, url: boundUrlField(entry.url) }));
|
|
184
|
+
const encoder = new TextEncoder();
|
|
185
|
+
const render = (entries: BrowserRuntimeDiagnostic[], truncated: boolean): string =>
|
|
186
|
+
JSON.stringify(
|
|
187
|
+
{
|
|
188
|
+
runtimeDiagnostics: entries,
|
|
189
|
+
runtimeDiagnosticsDropped,
|
|
190
|
+
...(truncated ? { runtimeDiagnosticsTruncated: true } : {}),
|
|
191
|
+
},
|
|
192
|
+
null,
|
|
193
|
+
2,
|
|
194
|
+
);
|
|
195
|
+
let kept = bounded;
|
|
196
|
+
let text = render(kept, false);
|
|
197
|
+
while (encoder.encode(text).byteLength > MAX_RUNTIME_DIAGNOSTICS_BLOCK_BYTES && kept.length > 1) {
|
|
198
|
+
kept = kept.slice(1);
|
|
199
|
+
text = render(kept, true);
|
|
200
|
+
}
|
|
201
|
+
// A single entry can exceed the budget only on type-violating input (every field is
|
|
202
|
+
// structurally bounded); the block still surfaces the entry, but truncation is
|
|
203
|
+
// never silent — flag the over-budget block explicitly.
|
|
204
|
+
if (encoder.encode(text).byteLength > MAX_RUNTIME_DIAGNOSTICS_BLOCK_BYTES) {
|
|
205
|
+
text = render(kept, true);
|
|
206
|
+
}
|
|
207
|
+
return text;
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
export class BrowserRuntimeDiagnosticsMailbox {
|
|
211
|
+
#entries: BrowserRuntimeDiagnostic[] = [];
|
|
212
|
+
#dropped = 0;
|
|
213
|
+
|
|
214
|
+
push(entry: BrowserRuntimeDiagnostic): void {
|
|
215
|
+
if (this.#entries.length === MAX_RUNTIME_DIAGNOSTICS) {
|
|
216
|
+
this.#entries.shift();
|
|
217
|
+
this.#dropped += 1;
|
|
218
|
+
}
|
|
219
|
+
this.#entries.push(entry);
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
drain(): { runtimeDiagnostics: BrowserRuntimeDiagnostic[]; runtimeDiagnosticsDropped: number } {
|
|
223
|
+
const runtimeDiagnostics = this.#entries.splice(0);
|
|
224
|
+
const runtimeDiagnosticsDropped = this.#dropped;
|
|
225
|
+
this.#dropped = 0;
|
|
226
|
+
return { runtimeDiagnostics, runtimeDiagnosticsDropped };
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
export async function instrumentBrowserRuntimeDiagnostics(
|
|
231
|
+
page: Page,
|
|
232
|
+
mailbox: BrowserRuntimeDiagnosticsMailbox,
|
|
233
|
+
): Promise<CDPSession> {
|
|
234
|
+
const session = await page.target().createCDPSession();
|
|
235
|
+
session.on("Runtime.exceptionThrown", event => {
|
|
236
|
+
mailbox.push(pageErrorDiagnostic(event, page.url()));
|
|
237
|
+
});
|
|
238
|
+
session.on("Runtime.consoleAPICalled", event => {
|
|
239
|
+
const diagnostic = consoleErrorDiagnostic(event, page.url());
|
|
240
|
+
if (diagnostic) mailbox.push(diagnostic);
|
|
241
|
+
});
|
|
242
|
+
try {
|
|
243
|
+
await session.send("Runtime.enable");
|
|
244
|
+
return session;
|
|
245
|
+
} catch (error) {
|
|
246
|
+
await session.detach().catch(() => undefined);
|
|
247
|
+
throw error;
|
|
248
|
+
}
|
|
249
|
+
}
|
|
@@ -51,6 +51,8 @@ export type WorkerInitPayload =
|
|
|
51
51
|
url?: string;
|
|
52
52
|
waitUntil?: "load" | "domcontentloaded" | "networkidle0" | "networkidle2";
|
|
53
53
|
timeoutMs: number;
|
|
54
|
+
/** Opt-in bounded page runtime diagnostics (extra CDP session + Runtime events). */
|
|
55
|
+
runtimeDiagnostics?: boolean;
|
|
54
56
|
}
|
|
55
57
|
| {
|
|
56
58
|
mode: "attach";
|
|
@@ -58,6 +60,8 @@ export type WorkerInitPayload =
|
|
|
58
60
|
safeDir: string;
|
|
59
61
|
targetId: string;
|
|
60
62
|
dialogs?: "accept" | "dismiss";
|
|
63
|
+
/** Opt-in bounded page runtime diagnostics (extra CDP session + Runtime events). */
|
|
64
|
+
runtimeDiagnostics?: boolean;
|
|
61
65
|
};
|
|
62
66
|
|
|
63
67
|
export type ToolReply = { ok: true; value: unknown } | { ok: false; error: RunErrorPayload };
|
|
@@ -81,6 +81,8 @@ export interface AcquireTabOptions {
|
|
|
81
81
|
dialogs?: DialogPolicy;
|
|
82
82
|
/** Owning session id so dispose can release only this session's tabs (F13). */
|
|
83
83
|
ownerId?: string;
|
|
84
|
+
/** Opt-in bounded page runtime diagnostics (extra CDP session + Runtime events per tab). */
|
|
85
|
+
runtimeDiagnostics?: boolean;
|
|
84
86
|
/** Internal recovery-only target id; makes a headless replacement attach, not open a page. */
|
|
85
87
|
recoveryTargetId?: string;
|
|
86
88
|
/** Recovery fence: replacement must not overwrite a tab installed after dead teardown. */
|
|
@@ -666,6 +668,7 @@ async function buildInitPayload(browser: BrowserHandle, opts: AcquireTabOptions)
|
|
|
666
668
|
safeDir,
|
|
667
669
|
targetId: opts.recoveryTargetId,
|
|
668
670
|
dialogs: opts.dialogs,
|
|
671
|
+
...(opts.runtimeDiagnostics ? { runtimeDiagnostics: true } : {}),
|
|
669
672
|
};
|
|
670
673
|
}
|
|
671
674
|
if (browser.kind.kind === "headless") {
|
|
@@ -679,6 +682,7 @@ async function buildInitPayload(browser: BrowserHandle, opts: AcquireTabOptions)
|
|
|
679
682
|
url: opts.url,
|
|
680
683
|
waitUntil: opts.waitUntil,
|
|
681
684
|
timeoutMs: opts.timeoutMs,
|
|
685
|
+
...(opts.runtimeDiagnostics ? { runtimeDiagnostics: true } : {}),
|
|
682
686
|
};
|
|
683
687
|
}
|
|
684
688
|
const page = await pickElectronTarget(browser.browser, opts.target);
|
|
@@ -689,6 +693,7 @@ async function buildInitPayload(browser: BrowserHandle, opts: AcquireTabOptions)
|
|
|
689
693
|
safeDir,
|
|
690
694
|
targetId,
|
|
691
695
|
dialogs: opts.dialogs,
|
|
696
|
+
...(opts.runtimeDiagnostics ? { runtimeDiagnostics: true } : {}),
|
|
692
697
|
};
|
|
693
698
|
}
|
|
694
699
|
export async function buildInitPayloadForTest(
|
|
@@ -7,6 +7,7 @@ import { Snowflake } from "@gajae-code/utils/snowflake";
|
|
|
7
7
|
import type { HTMLElement } from "linkedom";
|
|
8
8
|
import type {
|
|
9
9
|
Browser,
|
|
10
|
+
CDPSession,
|
|
10
11
|
Dialog,
|
|
11
12
|
ElementHandle,
|
|
12
13
|
HTTPResponse,
|
|
@@ -27,6 +28,11 @@ import {
|
|
|
27
28
|
loadPuppeteerInWorker,
|
|
28
29
|
} from "./launch";
|
|
29
30
|
import { extractReadableFromHtml, type ReadableFormat, type ReadableResult } from "./readable";
|
|
31
|
+
import {
|
|
32
|
+
BrowserRuntimeDiagnosticsMailbox,
|
|
33
|
+
instrumentBrowserRuntimeDiagnostics,
|
|
34
|
+
serializeRuntimeDiagnostics,
|
|
35
|
+
} from "./runtime-diagnostics";
|
|
30
36
|
import { formatScreenshot } from "./screenshot-format";
|
|
31
37
|
import type {
|
|
32
38
|
Observation,
|
|
@@ -75,6 +81,16 @@ const INTERACTIVE_AX_ROLES = new Set([
|
|
|
75
81
|
|
|
76
82
|
const LEGACY_SELECTOR_PREFIXES = ["p-aria/", "p-text/", "p-xpath/", "p-pierce/"] as const;
|
|
77
83
|
|
|
84
|
+
/**
|
|
85
|
+
* Test seam: override the worker's puppeteer loader so WorkerCore-level tests can
|
|
86
|
+
* inject a fake browser/page without a live Chromium. Mirrors the supervisor's
|
|
87
|
+
* `__setAcquireTabWorkerDepsForTest` pattern.
|
|
88
|
+
*/
|
|
89
|
+
let loadPuppeteerInWorkerForTest: typeof loadPuppeteerInWorker | undefined;
|
|
90
|
+
export function __setLoadPuppeteerInWorkerForTest(loader: typeof loadPuppeteerInWorker | undefined): void {
|
|
91
|
+
loadPuppeteerInWorkerForTest = loader;
|
|
92
|
+
}
|
|
93
|
+
|
|
78
94
|
type DialogPolicy = "accept" | "dismiss";
|
|
79
95
|
type DragTarget = string | { readonly x: number; readonly y: number };
|
|
80
96
|
type ActionabilityResult = { ok: true; x: number; y: number } | { ok: false; reason: string };
|
|
@@ -398,6 +414,8 @@ export class WorkerCore {
|
|
|
398
414
|
#transport: Transport;
|
|
399
415
|
#browser?: Browser;
|
|
400
416
|
#page?: Page;
|
|
417
|
+
#runtimeDiagnostics = new BrowserRuntimeDiagnosticsMailbox();
|
|
418
|
+
#runtimeDiagnosticsSession?: CDPSession;
|
|
401
419
|
#targetId?: string;
|
|
402
420
|
#elementCache = new Map<number, ElementHandle>();
|
|
403
421
|
#elementCounter = 0;
|
|
@@ -447,7 +465,7 @@ export class WorkerCore {
|
|
|
447
465
|
async #init(payload: WorkerInitPayload): Promise<void> {
|
|
448
466
|
try {
|
|
449
467
|
this.#mode = payload.mode;
|
|
450
|
-
const puppeteer = await loadPuppeteerInWorker(payload.safeDir);
|
|
468
|
+
const puppeteer = await (loadPuppeteerInWorkerForTest ?? loadPuppeteerInWorker)(payload.safeDir);
|
|
451
469
|
this.#browser = await puppeteer.connect({
|
|
452
470
|
browserWSEndpoint: payload.browserWSEndpoint,
|
|
453
471
|
defaultViewport: null,
|
|
@@ -457,6 +475,7 @@ export class WorkerCore {
|
|
|
457
475
|
this.#page = await this.#browser.newPage();
|
|
458
476
|
await applyStealthPatches(this.#browser, this.#page, { browserSession: null, override: null }, payload.geo);
|
|
459
477
|
await applyViewport(this.#page, payload.viewport);
|
|
478
|
+
if (payload.runtimeDiagnostics) await this.#instrumentRuntimeDiagnostics(this.#page);
|
|
460
479
|
if (payload.dialogs) this.#applyDialogPolicy(payload.dialogs);
|
|
461
480
|
if (payload.url) {
|
|
462
481
|
await this.#page.goto(payload.url, {
|
|
@@ -467,6 +486,7 @@ export class WorkerCore {
|
|
|
467
486
|
}
|
|
468
487
|
} else {
|
|
469
488
|
this.#page = await this.#findAttachedPage(payload.targetId);
|
|
489
|
+
if (payload.runtimeDiagnostics) await this.#instrumentRuntimeDiagnostics(this.#page);
|
|
470
490
|
if (payload.dialogs) this.#applyDialogPolicy(payload.dialogs);
|
|
471
491
|
}
|
|
472
492
|
this.#targetId = await targetIdForPage(this.#page);
|
|
@@ -517,6 +537,16 @@ export class WorkerCore {
|
|
|
517
537
|
this.#dialogHandler = handler;
|
|
518
538
|
}
|
|
519
539
|
|
|
540
|
+
async #instrumentRuntimeDiagnostics(page: Page): Promise<void> {
|
|
541
|
+
try {
|
|
542
|
+
this.#runtimeDiagnosticsSession = await instrumentBrowserRuntimeDiagnostics(page, this.#runtimeDiagnostics);
|
|
543
|
+
} catch (error) {
|
|
544
|
+
this.#log("debug", "Failed to instrument browser runtime diagnostics", {
|
|
545
|
+
error: error instanceof Error ? error.message : String(error),
|
|
546
|
+
});
|
|
547
|
+
}
|
|
548
|
+
}
|
|
549
|
+
|
|
520
550
|
async #postReadyInfo(): Promise<void> {
|
|
521
551
|
try {
|
|
522
552
|
this.#transport.send({ type: "ready", info: await this.#currentReadyInfo() });
|
|
@@ -581,6 +611,10 @@ export class WorkerCore {
|
|
|
581
611
|
cancelRejection,
|
|
582
612
|
]);
|
|
583
613
|
await this.#postReadyInfo();
|
|
614
|
+
const drained = this.#runtimeDiagnostics.drain();
|
|
615
|
+
if (drained.runtimeDiagnostics.length || drained.runtimeDiagnosticsDropped) {
|
|
616
|
+
displays.push({ type: "text", text: serializeRuntimeDiagnostics(drained) });
|
|
617
|
+
}
|
|
584
618
|
this.#transport.send({
|
|
585
619
|
type: "result",
|
|
586
620
|
id: msg.id,
|
|
@@ -1034,6 +1068,7 @@ export class WorkerCore {
|
|
|
1034
1068
|
this.#clearElementCache();
|
|
1035
1069
|
const page = this.#page;
|
|
1036
1070
|
if (this.#dialogHandler && page && !page.isClosed()) page.off("dialog", this.#dialogHandler);
|
|
1071
|
+
await this.#runtimeDiagnosticsSession?.detach().catch(() => undefined);
|
|
1037
1072
|
if (this.#mode === "headless" && page && !page.isClosed()) await page.close().catch(() => undefined);
|
|
1038
1073
|
if (this.#browser?.connected) this.#browser.disconnect();
|
|
1039
1074
|
this.#transport.send({ type: "closed" });
|
package/src/tools/browser.ts
CHANGED
|
@@ -87,6 +87,12 @@ const browserSchema = z.object({
|
|
|
87
87
|
.enum(["accept", "dismiss"] as const)
|
|
88
88
|
.describe("auto-handle dialogs")
|
|
89
89
|
.optional(),
|
|
90
|
+
diagnostics: z
|
|
91
|
+
.boolean()
|
|
92
|
+
.describe(
|
|
93
|
+
"opt-in: capture bounded page runtime diagnostics (page exceptions and console.error metadata) in the next successful run/act response",
|
|
94
|
+
)
|
|
95
|
+
.optional(),
|
|
90
96
|
code: z.string().describe("js body to run in tab").optional(),
|
|
91
97
|
actions: z.array(actionStepSchema).describe("structured action steps for action 'act'").optional(),
|
|
92
98
|
timeout: z.number().default(30).describe("timeout in seconds (default 30, max 300)").optional(),
|
|
@@ -258,6 +264,7 @@ export class BrowserTool implements AgentTool<typeof browserSchema, BrowserToolD
|
|
|
258
264
|
target: params.app?.target,
|
|
259
265
|
timeoutMs,
|
|
260
266
|
dialogs: params.dialogs,
|
|
267
|
+
runtimeDiagnostics: params.diagnostics === true,
|
|
261
268
|
signal,
|
|
262
269
|
}),
|
|
263
270
|
);
|