pi-agent-browser-native 0.2.48 → 0.2.50

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.
Files changed (189) hide show
  1. package/CHANGELOG.md +27 -1
  2. package/README.md +21 -11
  3. package/dist/extensions/agent-browser/index.js +808 -0
  4. package/dist/extensions/agent-browser/lib/argv-descriptor.js +71 -0
  5. package/dist/extensions/agent-browser/lib/argv-grammar.js +121 -0
  6. package/dist/extensions/agent-browser/lib/bash-guard.js +190 -0
  7. package/dist/extensions/agent-browser/lib/command-policy.js +85 -0
  8. package/dist/extensions/agent-browser/lib/command-taxonomy.js +302 -0
  9. package/dist/extensions/agent-browser/lib/config-policy.js +669 -0
  10. package/dist/extensions/agent-browser/lib/config.js +122 -0
  11. package/dist/extensions/agent-browser/lib/electron/cdp.js +51 -0
  12. package/dist/extensions/agent-browser/lib/electron/cleanup.js +212 -0
  13. package/dist/extensions/agent-browser/lib/electron/discovery.js +633 -0
  14. package/dist/extensions/agent-browser/lib/electron/launch.js +351 -0
  15. package/{extensions/agent-browser/lib/electron/text.ts → dist/extensions/agent-browser/lib/electron/text.js} +5 -5
  16. package/dist/extensions/agent-browser/lib/executable-path.js +20 -0
  17. package/dist/extensions/agent-browser/lib/fs-utils.js +18 -0
  18. package/dist/extensions/agent-browser/lib/input-modes/electron.js +165 -0
  19. package/dist/extensions/agent-browser/lib/input-modes/job.js +519 -0
  20. package/dist/extensions/agent-browser/lib/input-modes/lookups.js +440 -0
  21. package/dist/extensions/agent-browser/lib/input-modes/params.js +164 -0
  22. package/dist/extensions/agent-browser/lib/input-modes/semantic-action.js +119 -0
  23. package/dist/extensions/agent-browser/lib/input-modes/shared.js +42 -0
  24. package/dist/extensions/agent-browser/lib/input-modes/types.js +21 -0
  25. package/dist/extensions/agent-browser/lib/input-modes.js +10 -0
  26. package/dist/extensions/agent-browser/lib/json-schema.js +58 -0
  27. package/dist/extensions/agent-browser/lib/launch-scoped-flags.js +59 -0
  28. package/dist/extensions/agent-browser/lib/navigation-policy.js +83 -0
  29. package/dist/extensions/agent-browser/lib/orchestration/batch-stdin.js +62 -0
  30. package/dist/extensions/agent-browser/lib/orchestration/browser-run/artifact-paths.js +39 -0
  31. package/dist/extensions/agent-browser/lib/orchestration/browser-run/click-dispatch.js +276 -0
  32. package/dist/extensions/agent-browser/lib/orchestration/browser-run/diagnostics.js +909 -0
  33. package/dist/extensions/agent-browser/lib/orchestration/browser-run/final-result.js +443 -0
  34. package/dist/extensions/agent-browser/lib/orchestration/browser-run/index.js +47 -0
  35. package/dist/extensions/agent-browser/lib/orchestration/browser-run/prepare/direct-anchor-download.js +141 -0
  36. package/dist/extensions/agent-browser/lib/orchestration/browser-run/prepare/network-page-filter.js +108 -0
  37. package/dist/extensions/agent-browser/lib/orchestration/browser-run/prepare/scroll-shims.js +112 -0
  38. package/dist/extensions/agent-browser/lib/orchestration/browser-run/prepare/snapshot-filter.js +158 -0
  39. package/dist/extensions/agent-browser/lib/orchestration/browser-run/prepare/wait-timeouts.js +54 -0
  40. package/dist/extensions/agent-browser/lib/orchestration/browser-run/prepare.js +762 -0
  41. package/dist/extensions/agent-browser/lib/orchestration/browser-run/process-output.js +491 -0
  42. package/dist/extensions/agent-browser/lib/orchestration/browser-run/prompt-guards.js +40 -0
  43. package/dist/extensions/agent-browser/lib/orchestration/browser-run/session-artifacts.js +5 -0
  44. package/dist/extensions/agent-browser/lib/orchestration/browser-run/session-state.js +731 -0
  45. package/dist/extensions/agent-browser/lib/orchestration/browser-run/types.js +1 -0
  46. package/dist/extensions/agent-browser/lib/orchestration/electron-host/index.js +718 -0
  47. package/dist/extensions/agent-browser/lib/orchestration/input-plan.js +247 -0
  48. package/dist/extensions/agent-browser/lib/orchestration/output-file.js +68 -0
  49. package/{extensions/agent-browser/lib/parsing.ts → dist/extensions/agent-browser/lib/parsing.js} +12 -11
  50. package/dist/extensions/agent-browser/lib/pi-tool-rendering.js +241 -0
  51. package/dist/extensions/agent-browser/lib/playbook.js +121 -0
  52. package/dist/extensions/agent-browser/lib/process.js +363 -0
  53. package/dist/extensions/agent-browser/lib/prompt-policy.js +91 -0
  54. package/dist/extensions/agent-browser/lib/results/action-recommendations.js +220 -0
  55. package/dist/extensions/agent-browser/lib/results/artifact-manifest.js +111 -0
  56. package/{extensions/agent-browser/lib/results/artifact-state.ts → dist/extensions/agent-browser/lib/results/artifact-state.js} +4 -8
  57. package/dist/extensions/agent-browser/lib/results/categories.js +76 -0
  58. package/dist/extensions/agent-browser/lib/results/confirmation.js +63 -0
  59. package/dist/extensions/agent-browser/lib/results/contracts.js +8 -0
  60. package/dist/extensions/agent-browser/lib/results/editable-ref-evidence.js +74 -0
  61. package/dist/extensions/agent-browser/lib/results/envelope.js +166 -0
  62. package/dist/extensions/agent-browser/lib/results/network-routes.js +92 -0
  63. package/dist/extensions/agent-browser/lib/results/network.js +73 -0
  64. package/dist/extensions/agent-browser/lib/results/next-actions.js +72 -0
  65. package/dist/extensions/agent-browser/lib/results/presentation/artifacts.js +515 -0
  66. package/dist/extensions/agent-browser/lib/results/presentation/batch.js +397 -0
  67. package/dist/extensions/agent-browser/lib/results/presentation/browser-profile-recovery.js +55 -0
  68. package/dist/extensions/agent-browser/lib/results/presentation/common.js +46 -0
  69. package/dist/extensions/agent-browser/lib/results/presentation/content.js +24 -0
  70. package/dist/extensions/agent-browser/lib/results/presentation/diagnostics.js +956 -0
  71. package/dist/extensions/agent-browser/lib/results/presentation/errors.js +205 -0
  72. package/dist/extensions/agent-browser/lib/results/presentation/large-output.js +134 -0
  73. package/dist/extensions/agent-browser/lib/results/presentation/navigation.js +159 -0
  74. package/dist/extensions/agent-browser/lib/results/presentation/registry.js +216 -0
  75. package/dist/extensions/agent-browser/lib/results/presentation/semantic-action.js +104 -0
  76. package/dist/extensions/agent-browser/lib/results/presentation/skills.js +152 -0
  77. package/dist/extensions/agent-browser/lib/results/presentation.js +177 -0
  78. package/dist/extensions/agent-browser/lib/results/recovery-actions.js +107 -0
  79. package/dist/extensions/agent-browser/lib/results/recovery-next-actions.js +50 -0
  80. package/dist/extensions/agent-browser/lib/results/selector-recovery.js +225 -0
  81. package/{extensions/agent-browser/lib/results/shared.ts → dist/extensions/agent-browser/lib/results/shared.js} +0 -1
  82. package/dist/extensions/agent-browser/lib/results/snapshot-high-value-controls.js +208 -0
  83. package/dist/extensions/agent-browser/lib/results/snapshot-refs.js +78 -0
  84. package/dist/extensions/agent-browser/lib/results/snapshot-segments.js +331 -0
  85. package/dist/extensions/agent-browser/lib/results/snapshot-spill.js +40 -0
  86. package/dist/extensions/agent-browser/lib/results/snapshot.js +264 -0
  87. package/dist/extensions/agent-browser/lib/results/text.js +40 -0
  88. package/{extensions/agent-browser/lib/results.ts → dist/extensions/agent-browser/lib/results.js} +2 -32
  89. package/dist/extensions/agent-browser/lib/runtime.js +855 -0
  90. package/dist/extensions/agent-browser/lib/session-page-state.js +411 -0
  91. package/dist/extensions/agent-browser/lib/string-enum-schema.js +13 -0
  92. package/dist/extensions/agent-browser/lib/temp.js +498 -0
  93. package/dist/extensions/agent-browser/lib/web-search.js +562 -0
  94. package/docs/ARCHITECTURE.md +5 -5
  95. package/docs/COMMAND_REFERENCE.md +4 -4
  96. package/docs/RELEASE.md +22 -11
  97. package/docs/REQUIREMENTS.md +1 -1
  98. package/docs/SUPPORT_MATRIX.md +5 -4
  99. package/docs/TOOL_CONTRACT.md +1 -1
  100. package/package.json +9 -5
  101. package/scripts/config.mjs +14 -20
  102. package/scripts/doctor.mjs +8 -7
  103. package/extensions/agent-browser/index.ts +0 -961
  104. package/extensions/agent-browser/lib/argv-descriptor.ts +0 -90
  105. package/extensions/agent-browser/lib/argv-grammar.ts +0 -128
  106. package/extensions/agent-browser/lib/bash-guard.ts +0 -205
  107. package/extensions/agent-browser/lib/command-policy.ts +0 -71
  108. package/extensions/agent-browser/lib/command-taxonomy.ts +0 -336
  109. package/extensions/agent-browser/lib/config-policy.js +0 -690
  110. package/extensions/agent-browser/lib/config.ts +0 -211
  111. package/extensions/agent-browser/lib/electron/cdp.ts +0 -69
  112. package/extensions/agent-browser/lib/electron/cleanup.ts +0 -235
  113. package/extensions/agent-browser/lib/electron/discovery.ts +0 -710
  114. package/extensions/agent-browser/lib/electron/launch.ts +0 -499
  115. package/extensions/agent-browser/lib/executable-path.ts +0 -19
  116. package/extensions/agent-browser/lib/fs-utils.ts +0 -18
  117. package/extensions/agent-browser/lib/input-modes/electron.ts +0 -170
  118. package/extensions/agent-browser/lib/input-modes/job.ts +0 -527
  119. package/extensions/agent-browser/lib/input-modes/lookups.ts +0 -447
  120. package/extensions/agent-browser/lib/input-modes/params.ts +0 -205
  121. package/extensions/agent-browser/lib/input-modes/semantic-action.ts +0 -127
  122. package/extensions/agent-browser/lib/input-modes/shared.ts +0 -46
  123. package/extensions/agent-browser/lib/input-modes/types.ts +0 -225
  124. package/extensions/agent-browser/lib/input-modes.ts +0 -45
  125. package/extensions/agent-browser/lib/json-schema.ts +0 -73
  126. package/extensions/agent-browser/lib/launch-scoped-flags.ts +0 -67
  127. package/extensions/agent-browser/lib/navigation-policy.ts +0 -95
  128. package/extensions/agent-browser/lib/orchestration/batch-stdin.ts +0 -65
  129. package/extensions/agent-browser/lib/orchestration/browser-run/artifact-paths.ts +0 -44
  130. package/extensions/agent-browser/lib/orchestration/browser-run/click-dispatch.ts +0 -280
  131. package/extensions/agent-browser/lib/orchestration/browser-run/diagnostics.ts +0 -914
  132. package/extensions/agent-browser/lib/orchestration/browser-run/final-result.ts +0 -521
  133. package/extensions/agent-browser/lib/orchestration/browser-run/index.ts +0 -53
  134. package/extensions/agent-browser/lib/orchestration/browser-run/prepare/direct-anchor-download.ts +0 -158
  135. package/extensions/agent-browser/lib/orchestration/browser-run/prepare/network-page-filter.ts +0 -116
  136. package/extensions/agent-browser/lib/orchestration/browser-run/prepare/scroll-shims.ts +0 -147
  137. package/extensions/agent-browser/lib/orchestration/browser-run/prepare/snapshot-filter.ts +0 -183
  138. package/extensions/agent-browser/lib/orchestration/browser-run/prepare/wait-timeouts.ts +0 -58
  139. package/extensions/agent-browser/lib/orchestration/browser-run/prepare.ts +0 -847
  140. package/extensions/agent-browser/lib/orchestration/browser-run/process-output.ts +0 -559
  141. package/extensions/agent-browser/lib/orchestration/browser-run/prompt-guards.ts +0 -47
  142. package/extensions/agent-browser/lib/orchestration/browser-run/session-artifacts.ts +0 -8
  143. package/extensions/agent-browser/lib/orchestration/browser-run/session-state.ts +0 -868
  144. package/extensions/agent-browser/lib/orchestration/browser-run/types.ts +0 -565
  145. package/extensions/agent-browser/lib/orchestration/electron-host/index.ts +0 -855
  146. package/extensions/agent-browser/lib/orchestration/input-plan.ts +0 -375
  147. package/extensions/agent-browser/lib/orchestration/output-file.ts +0 -86
  148. package/extensions/agent-browser/lib/pi-tool-rendering.ts +0 -267
  149. package/extensions/agent-browser/lib/playbook.ts +0 -142
  150. package/extensions/agent-browser/lib/process.ts +0 -516
  151. package/extensions/agent-browser/lib/prompt-policy.ts +0 -105
  152. package/extensions/agent-browser/lib/results/action-recommendations.ts +0 -264
  153. package/extensions/agent-browser/lib/results/artifact-manifest.ts +0 -111
  154. package/extensions/agent-browser/lib/results/categories.ts +0 -106
  155. package/extensions/agent-browser/lib/results/confirmation.ts +0 -76
  156. package/extensions/agent-browser/lib/results/contracts.ts +0 -241
  157. package/extensions/agent-browser/lib/results/editable-ref-evidence.ts +0 -72
  158. package/extensions/agent-browser/lib/results/envelope.ts +0 -195
  159. package/extensions/agent-browser/lib/results/network-routes.ts +0 -83
  160. package/extensions/agent-browser/lib/results/network.ts +0 -78
  161. package/extensions/agent-browser/lib/results/next-actions.ts +0 -117
  162. package/extensions/agent-browser/lib/results/presentation/artifacts.ts +0 -588
  163. package/extensions/agent-browser/lib/results/presentation/batch.ts +0 -450
  164. package/extensions/agent-browser/lib/results/presentation/browser-profile-recovery.ts +0 -67
  165. package/extensions/agent-browser/lib/results/presentation/common.ts +0 -53
  166. package/extensions/agent-browser/lib/results/presentation/content.ts +0 -36
  167. package/extensions/agent-browser/lib/results/presentation/diagnostics.ts +0 -923
  168. package/extensions/agent-browser/lib/results/presentation/errors.ts +0 -227
  169. package/extensions/agent-browser/lib/results/presentation/large-output.ts +0 -182
  170. package/extensions/agent-browser/lib/results/presentation/navigation.ts +0 -184
  171. package/extensions/agent-browser/lib/results/presentation/registry.ts +0 -242
  172. package/extensions/agent-browser/lib/results/presentation/semantic-action.ts +0 -131
  173. package/extensions/agent-browser/lib/results/presentation/skills.ts +0 -143
  174. package/extensions/agent-browser/lib/results/presentation.ts +0 -257
  175. package/extensions/agent-browser/lib/results/recovery-actions.ts +0 -139
  176. package/extensions/agent-browser/lib/results/recovery-next-actions.ts +0 -71
  177. package/extensions/agent-browser/lib/results/selector-recovery.ts +0 -320
  178. package/extensions/agent-browser/lib/results/snapshot-high-value-controls.ts +0 -273
  179. package/extensions/agent-browser/lib/results/snapshot-refs.ts +0 -100
  180. package/extensions/agent-browser/lib/results/snapshot-segments.ts +0 -366
  181. package/extensions/agent-browser/lib/results/snapshot-spill.ts +0 -63
  182. package/extensions/agent-browser/lib/results/snapshot.ts +0 -329
  183. package/extensions/agent-browser/lib/results/text.ts +0 -40
  184. package/extensions/agent-browser/lib/runtime.ts +0 -988
  185. package/extensions/agent-browser/lib/session-page-state.ts +0 -512
  186. package/extensions/agent-browser/lib/string-enum-schema.ts +0 -20
  187. package/extensions/agent-browser/lib/temp.ts +0 -577
  188. package/extensions/agent-browser/lib/web-search.ts +0 -728
  189. /package/{extensions/agent-browser/lib/orchestration/browser-run.ts → dist/extensions/agent-browser/lib/orchestration/browser-run.js} +0 -0
@@ -0,0 +1,669 @@
1
+ // @ts-check
2
+ /**
3
+ * Purpose: Canonical pi-agent-browser-native config policy shared by runtime and setup CLI.
4
+ * Responsibilities: Own config paths, provider descriptors, credential source parsing, layer validation/merge, status projection, and redacted summaries.
5
+ * Scope: Pure configuration policy plus synchronous status loading; secret command execution and browser/web-search runtime calls live elsewhere.
6
+ */
7
+ import { existsSync, readFileSync } from "node:fs";
8
+ import { homedir } from "node:os";
9
+ import { join, resolve } from "node:path";
10
+ /** @typedef {"explicit-only" | "authenticated-only" | "always"} BrowserDefaultProfilePolicy */
11
+ /** @typedef {"global" | "project" | "override" | "env-fallback"} AgentBrowserConfigScope */
12
+ /** @typedef {"global" | "project" | "override"} ConfigLayerScope */
13
+ /** @typedef {"literal" | "env" | "command"} CredentialSourceKind */
14
+ /** @typedef {"exa" | "brave"} WebSearchProvider */
15
+ /** @typedef {"exaApiKey" | "braveApiKey"} WebSearchProviderConfigKey */
16
+ /** @typedef {{ provider: WebSearchProvider; apiKeyEnv: string; configKey: WebSearchProviderConfigKey; label: string }} WebSearchProviderDescriptor */
17
+ /** @typedef {{ name: string; policy?: BrowserDefaultProfilePolicy }} BrowserDefaultProfileConfig */
18
+ /** @typedef {{ enabled?: boolean; preferredProvider?: WebSearchProvider; braveApiKey?: string; exaApiKey?: string }} WebSearchConfig */
19
+ /** @typedef {{ defaultProfile?: BrowserDefaultProfileConfig; executablePath?: string; defaultLaunchArgs?: string[] }} BrowserConfig */
20
+ /** @typedef {{ version?: 1; webSearch?: WebSearchConfig; browser?: BrowserConfig }} AgentBrowserConfig */
21
+ /** @typedef {{ config: AgentBrowserConfig; path: string; scope: ConfigLayerScope }} ConfigLayer */
22
+ /** @typedef {{ kind: CredentialSourceKind; provider?: WebSearchProvider; rawValue: string; scope: AgentBrowserConfigScope }} CredentialSource */
23
+ /** @typedef {{ global: string; project: string; override?: string }} AgentBrowserConfigPaths */
24
+ /** @typedef {{ cwd?: string; env?: NodeJS.ProcessEnv; includeProjectConfig?: boolean }} AgentBrowserConfigLoadOptions */
25
+ /** @typedef {{ browserDefaultProfile?: Required<BrowserDefaultProfileConfig>; browserDefaultProfileScope?: ConfigLayerScope; browserExecutablePath?: string; browserExecutablePathScope?: ConfigLayerScope; trustedBrowserDefaultProfile?: Required<BrowserDefaultProfileConfig>; trustedBrowserDefaultProfileScope?: ConfigLayerScope; trustedBrowserExecutablePath?: string; trustedBrowserExecutablePathScope?: ConfigLayerScope; config: AgentBrowserConfig; webSearchCredentialSources: Partial<Record<WebSearchProvider, CredentialSource>>; webSearchEnabled: boolean; webSearchPreferredProvider: WebSearchProvider; errors: string[]; layers: ConfigLayer[]; paths: AgentBrowserConfigPaths; projectConfigIncluded: boolean; warnings: string[] }} AgentBrowserConfigState */
26
+ /** @typedef {{ scope: string; path: string; exists: boolean }} ConfigFileSummary */
27
+ export const AGENT_BROWSER_CONFIG_ENV = "PI_AGENT_BROWSER_CONFIG";
28
+ export const BRAVE_API_KEY_ENV = "BRAVE_API_KEY";
29
+ export const EXA_API_KEY_ENV = "EXA_API_KEY";
30
+ export const CONFIG_RELATIVE_PATH = /** @type {const} */ ([".pi", "config", "pi-agent-browser-native", "config.json"]);
31
+ export const GLOBAL_CONFIG_RELATIVE_PATH = /** @type {const} */ ([".pi", "config", "pi-agent-browser-native", "config.json"]);
32
+ export const SECRET_COMMAND_TIMEOUT_MS = 15_000;
33
+ /** @type {Readonly<Record<WebSearchProvider, WebSearchProviderDescriptor>>} */
34
+ export const WEB_SEARCH_PROVIDER_DESCRIPTORS = Object.freeze({
35
+ exa: Object.freeze({
36
+ provider: "exa",
37
+ apiKeyEnv: EXA_API_KEY_ENV,
38
+ configKey: "exaApiKey",
39
+ label: "Exa",
40
+ }),
41
+ brave: Object.freeze({
42
+ provider: "brave",
43
+ apiKeyEnv: BRAVE_API_KEY_ENV,
44
+ configKey: "braveApiKey",
45
+ label: "Brave Search",
46
+ }),
47
+ });
48
+ /** @type {readonly WebSearchProvider[]} */
49
+ export const WEB_SEARCH_PROVIDERS = Object.freeze(["exa", "brave"]);
50
+ /** @type {WebSearchProvider} */
51
+ export const DEFAULT_WEB_SEARCH_PROVIDER = "exa";
52
+ /** @type {Readonly<Record<WebSearchProvider, WebSearchProviderConfigKey>>} */
53
+ export const WEB_SEARCH_PROVIDER_CONFIG_KEYS = Object.freeze({
54
+ exa: WEB_SEARCH_PROVIDER_DESCRIPTORS.exa.configKey,
55
+ brave: WEB_SEARCH_PROVIDER_DESCRIPTORS.brave.configKey,
56
+ });
57
+ /** @type {Readonly<Record<WebSearchProvider, string>>} */
58
+ export const WEB_SEARCH_PROVIDER_ENV_VARS = Object.freeze({
59
+ exa: WEB_SEARCH_PROVIDER_DESCRIPTORS.exa.apiKeyEnv,
60
+ brave: WEB_SEARCH_PROVIDER_DESCRIPTORS.brave.apiKeyEnv,
61
+ });
62
+ /**
63
+ * @param {unknown} value
64
+ * @returns {value is WebSearchProvider}
65
+ */
66
+ export function isWebSearchProvider(value) {
67
+ return typeof value === "string" && WEB_SEARCH_PROVIDERS.includes(/** @type {WebSearchProvider} */ (value));
68
+ }
69
+ /**
70
+ * @param {WebSearchProvider} provider
71
+ * @returns {WebSearchProviderDescriptor}
72
+ */
73
+ export function getWebSearchProviderDescriptor(provider) {
74
+ const descriptor = WEB_SEARCH_PROVIDER_DESCRIPTORS[provider];
75
+ if (!descriptor)
76
+ throw new Error(`Unknown web-search provider: ${String(provider)}`);
77
+ return descriptor;
78
+ }
79
+ /** @param {WebSearchProvider} provider */
80
+ export function getWebSearchProviderLabel(provider) {
81
+ return getWebSearchProviderDescriptor(provider).label;
82
+ }
83
+ /** @param {WebSearchProvider} provider */
84
+ export function getWebSearchProviderEnvVar(provider) {
85
+ return getWebSearchProviderDescriptor(provider).apiKeyEnv;
86
+ }
87
+ /**
88
+ * @param {WebSearchProvider} provider
89
+ * @returns {WebSearchProviderConfigKey}
90
+ */
91
+ export function getWebSearchProviderConfigKey(provider) {
92
+ return getWebSearchProviderDescriptor(provider).configKey;
93
+ }
94
+ /** @param {NodeJS.ProcessEnv} [env] */
95
+ export function getGlobalAgentBrowserConfigPath(env = process.env) {
96
+ const home = env.HOME?.trim() || env.USERPROFILE?.trim() || homedir();
97
+ return join(home, ...GLOBAL_CONFIG_RELATIVE_PATH);
98
+ }
99
+ /** @param {string} [cwd] */
100
+ export function getProjectAgentBrowserConfigPath(cwd = process.cwd()) {
101
+ return resolve(cwd, ...CONFIG_RELATIVE_PATH);
102
+ }
103
+ /**
104
+ * @param {{ cwd?: string; env?: NodeJS.ProcessEnv }} [options]
105
+ * @returns {AgentBrowserConfigPaths}
106
+ */
107
+ export function getAgentBrowserConfigPaths(options = {}) {
108
+ const env = options.env ?? process.env;
109
+ const override = env[AGENT_BROWSER_CONFIG_ENV]?.trim();
110
+ return {
111
+ global: getGlobalAgentBrowserConfigPath(env),
112
+ project: getProjectAgentBrowserConfigPath(options.cwd),
113
+ ...(override ? { override: resolve(override) } : {}),
114
+ };
115
+ }
116
+ /**
117
+ * @param {AgentBrowserConfig} base
118
+ * @param {AgentBrowserConfig} override
119
+ * @returns {AgentBrowserConfig}
120
+ */
121
+ export function mergeAgentBrowserConfig(base, override) {
122
+ return {
123
+ ...base,
124
+ ...override,
125
+ browser: {
126
+ ...(base.browser ?? {}),
127
+ ...(override.browser ?? {}),
128
+ defaultProfile: override.browser?.defaultProfile ?? base.browser?.defaultProfile,
129
+ },
130
+ webSearch: {
131
+ ...(base.webSearch ?? {}),
132
+ ...(override.webSearch ?? {}),
133
+ },
134
+ };
135
+ }
136
+ /**
137
+ * @param {unknown} value
138
+ * @returns {value is Record<string, unknown>}
139
+ */
140
+ function isRecord(value) {
141
+ return typeof value === "object" && value !== null && !Array.isArray(value);
142
+ }
143
+ /**
144
+ * @param {unknown} value
145
+ * @param {string} path
146
+ * @param {string[]} errors
147
+ */
148
+ function validateString(value, path, errors) {
149
+ if (value === undefined)
150
+ return undefined;
151
+ if (typeof value !== "string") {
152
+ errors.push(`${path} must be a string.`);
153
+ return undefined;
154
+ }
155
+ return value;
156
+ }
157
+ /**
158
+ * @param {unknown} value
159
+ * @param {string} path
160
+ * @param {string[]} errors
161
+ * @returns {string[] | undefined}
162
+ */
163
+ function validateStringArray(value, path, errors) {
164
+ if (value === undefined)
165
+ return undefined;
166
+ if (!Array.isArray(value) || value.some((entry) => typeof entry !== "string")) {
167
+ errors.push(`${path} must be an array of strings.`);
168
+ return undefined;
169
+ }
170
+ return value;
171
+ }
172
+ /**
173
+ * @param {unknown} value
174
+ * @param {string} path
175
+ * @param {string[]} errors
176
+ */
177
+ function validateBoolean(value, path, errors) {
178
+ if (value === undefined)
179
+ return undefined;
180
+ if (typeof value !== "boolean") {
181
+ errors.push(`${path} must be a boolean.`);
182
+ return undefined;
183
+ }
184
+ return value;
185
+ }
186
+ /**
187
+ * @param {unknown} value
188
+ * @param {string} path
189
+ * @param {string[]} errors
190
+ * @returns {WebSearchProvider | undefined}
191
+ */
192
+ export function validateWebSearchProvider(value, path, errors) {
193
+ if (value === undefined)
194
+ return undefined;
195
+ const provider = validateString(value, path, errors)?.trim();
196
+ if (provider === undefined)
197
+ return undefined;
198
+ if (!isWebSearchProvider(provider)) {
199
+ errors.push(`${path} must be one of ${WEB_SEARCH_PROVIDERS.join(", ")}.`);
200
+ return undefined;
201
+ }
202
+ return provider;
203
+ }
204
+ /**
205
+ * @param {unknown} value
206
+ * @param {string} path
207
+ * @param {string[]} errors
208
+ * @returns {BrowserDefaultProfileConfig | undefined}
209
+ */
210
+ function validateBrowserDefaultProfile(value, path, errors) {
211
+ if (value === undefined)
212
+ return undefined;
213
+ if (!isRecord(value)) {
214
+ errors.push(`${path} must be an object.`);
215
+ return undefined;
216
+ }
217
+ const name = validateString(value.name, `${path}.name`, errors)?.trim();
218
+ if (!name) {
219
+ errors.push(`${path}.name must not be blank.`);
220
+ return undefined;
221
+ }
222
+ const rawPolicy = validateString(value.policy, `${path}.policy`, errors);
223
+ const policy = rawPolicy ?? "authenticated-only";
224
+ if (!["explicit-only", "authenticated-only", "always"].includes(policy)) {
225
+ errors.push(`${path}.policy must be one of explicit-only, authenticated-only, always.`);
226
+ return undefined;
227
+ }
228
+ return { name, policy: /** @type {BrowserDefaultProfilePolicy} */ (policy) };
229
+ }
230
+ /** @param {string} rawValue */
231
+ export function isPlaintextCredentialValue(rawValue) {
232
+ const trimmed = rawValue.trim();
233
+ return Boolean(trimmed) && !trimmed.startsWith("!") && !trimmed.startsWith("$");
234
+ }
235
+ /**
236
+ * @param {string} rawValue
237
+ * @param {WebSearchProvider} provider
238
+ */
239
+ export function isProjectSafeCredentialValueForProvider(rawValue, provider) {
240
+ void provider;
241
+ return rawValue.trim().length > 0;
242
+ }
243
+ /**
244
+ * @param {unknown} value
245
+ * @param {string} path
246
+ * @param {ConfigLayerScope} scope
247
+ * @param {string[]} errors
248
+ * @param {string[]} warnings
249
+ * @returns {AgentBrowserConfig | undefined}
250
+ */
251
+ export function validateAgentBrowserConfig(value, path, scope, errors, warnings) {
252
+ if (!isRecord(value)) {
253
+ errors.push(`${path} must contain a JSON object.`);
254
+ return undefined;
255
+ }
256
+ if (value.version !== undefined && value.version !== 1) {
257
+ errors.push(`${path}.version must be 1 when present.`);
258
+ }
259
+ /** @type {AgentBrowserConfig} */
260
+ const config = value.version === 1 ? { version: 1 } : {};
261
+ if (value.webSearch !== undefined) {
262
+ if (!isRecord(value.webSearch)) {
263
+ errors.push(`${path}.webSearch must be an object.`);
264
+ }
265
+ else {
266
+ /** @type {NonNullable<AgentBrowserConfig["webSearch"]>} */
267
+ const webSearch = {};
268
+ const enabled = validateBoolean(value.webSearch.enabled, `${path}.webSearch.enabled`, errors);
269
+ if (enabled !== undefined)
270
+ webSearch.enabled = enabled;
271
+ const preferredProvider = validateWebSearchProvider(value.webSearch.preferredProvider, `${path}.webSearch.preferredProvider`, errors);
272
+ if (preferredProvider)
273
+ webSearch.preferredProvider = preferredProvider;
274
+ for (const provider of WEB_SEARCH_PROVIDERS) {
275
+ const descriptor = getWebSearchProviderDescriptor(provider);
276
+ const apiKey = validateString(value.webSearch[descriptor.configKey], `${path}.webSearch.${descriptor.configKey}`, errors);
277
+ if (apiKey !== undefined) {
278
+ webSearch[descriptor.configKey] = apiKey;
279
+ }
280
+ }
281
+ if (Object.keys(webSearch).length > 0)
282
+ config.webSearch = webSearch;
283
+ }
284
+ }
285
+ if (value.browser !== undefined) {
286
+ if (!isRecord(value.browser)) {
287
+ errors.push(`${path}.browser must be an object.`);
288
+ }
289
+ else {
290
+ config.browser = {};
291
+ const defaultProfile = validateBrowserDefaultProfile(value.browser.defaultProfile, `${path}.browser.defaultProfile`, errors);
292
+ if (defaultProfile) {
293
+ config.browser.defaultProfile = defaultProfile;
294
+ }
295
+ const executablePath = validateString(value.browser.executablePath, `${path}.browser.executablePath`, errors)?.trim();
296
+ if (executablePath) {
297
+ config.browser.executablePath = executablePath;
298
+ }
299
+ const defaultLaunchArgs = validateStringArray(value.browser.defaultLaunchArgs, `${path}.browser.defaultLaunchArgs`, errors);
300
+ if (defaultLaunchArgs) {
301
+ config.browser.defaultLaunchArgs = defaultLaunchArgs;
302
+ warnings.push(`${path}.browser.defaultLaunchArgs is recorded for future use; current releases do not auto-inject default launch args.`);
303
+ }
304
+ }
305
+ }
306
+ for (const key of Object.keys(value)) {
307
+ if (!["version", "webSearch", "browser"].includes(key)) {
308
+ warnings.push(`${path}.${key} is not a recognized pi-agent-browser-native config field and was ignored.`);
309
+ }
310
+ }
311
+ return config;
312
+ }
313
+ /**
314
+ * @param {string} raw
315
+ * @param {string} path
316
+ * @param {ConfigLayerScope} scope
317
+ * @param {string[]} errors
318
+ * @param {string[]} warnings
319
+ * @returns {ConfigLayer | undefined}
320
+ */
321
+ export function parseAgentBrowserConfigLayer(raw, path, scope, errors, warnings) {
322
+ let parsed;
323
+ try {
324
+ parsed = JSON.parse(raw);
325
+ }
326
+ catch (error) {
327
+ errors.push(`Could not parse ${scope} config ${path}: ${error instanceof Error ? error.message : String(error)}`);
328
+ return undefined;
329
+ }
330
+ const config = validateAgentBrowserConfig(parsed, path, scope, errors, warnings);
331
+ return config ? { config, path, scope } : undefined;
332
+ }
333
+ /**
334
+ * @param {string} rawValue
335
+ * @param {AgentBrowserConfigScope} scope
336
+ * @param {WebSearchProvider} [provider]
337
+ * @returns {CredentialSource | undefined}
338
+ */
339
+ export function classifyCredentialSource(rawValue, scope, provider) {
340
+ const trimmed = rawValue.trim();
341
+ if (!trimmed)
342
+ return undefined;
343
+ if (trimmed.startsWith("!"))
344
+ return { kind: "command", provider, rawValue: trimmed, scope };
345
+ if (trimmed.includes("$"))
346
+ return { kind: "env", provider, rawValue: trimmed, scope };
347
+ return { kind: "literal", provider, rawValue: trimmed, scope };
348
+ }
349
+ /**
350
+ * @param {AgentBrowserConfig} config
351
+ * @returns {Required<BrowserDefaultProfileConfig> | undefined}
352
+ */
353
+ function getBrowserDefaultProfile(config) {
354
+ const profile = config.browser?.defaultProfile;
355
+ if (!profile?.name.trim())
356
+ return undefined;
357
+ return { name: profile.name.trim(), policy: profile.policy ?? "authenticated-only" };
358
+ }
359
+ /** @param {AgentBrowserConfig} config */
360
+ function getBrowserExecutablePath(config) {
361
+ const executablePath = config.browser?.executablePath?.trim();
362
+ return executablePath || undefined;
363
+ }
364
+ /**
365
+ * @param {ConfigLayer[]} layers
366
+ * @returns {ConfigLayerScope | undefined}
367
+ */
368
+ function getBrowserDefaultProfileScope(layers) {
369
+ for (let index = layers.length - 1; index >= 0; index -= 1) {
370
+ const layer = layers[index];
371
+ if (layer?.config.browser?.defaultProfile !== undefined)
372
+ return layer.scope;
373
+ }
374
+ return undefined;
375
+ }
376
+ /**
377
+ * @param {ConfigLayer[]} layers
378
+ * @returns {ConfigLayerScope | undefined}
379
+ */
380
+ function getBrowserExecutablePathScope(layers) {
381
+ for (let index = layers.length - 1; index >= 0; index -= 1) {
382
+ const layer = layers[index];
383
+ if (layer?.config.browser?.executablePath !== undefined)
384
+ return layer.scope;
385
+ }
386
+ return undefined;
387
+ }
388
+ /**
389
+ * @param {ConfigLayer[]} layers
390
+ * @returns {{ profile: Required<BrowserDefaultProfileConfig>; scope: ConfigLayerScope } | undefined}
391
+ */
392
+ function getTrustedBrowserDefaultProfile(layers) {
393
+ for (let index = layers.length - 1; index >= 0; index -= 1) {
394
+ const layer = layers[index];
395
+ if (!layer)
396
+ continue;
397
+ const profile = getBrowserDefaultProfile(layer.config);
398
+ if (profile)
399
+ return { profile, scope: layer.scope };
400
+ }
401
+ return undefined;
402
+ }
403
+ /**
404
+ * @param {ConfigLayer[]} layers
405
+ * @returns {{ executablePath: string; scope: ConfigLayerScope } | undefined}
406
+ */
407
+ function getTrustedBrowserExecutablePath(layers) {
408
+ for (let index = layers.length - 1; index >= 0; index -= 1) {
409
+ const layer = layers[index];
410
+ if (!layer)
411
+ continue;
412
+ const executablePath = getBrowserExecutablePath(layer.config);
413
+ if (executablePath)
414
+ return { executablePath, scope: layer.scope };
415
+ }
416
+ return undefined;
417
+ }
418
+ /**
419
+ * @param {ConfigLayer[]} layers
420
+ * @param {WebSearchProviderConfigKey} key
421
+ * @returns {AgentBrowserConfigScope}
422
+ */
423
+ function getWebSearchCredentialScope(layers, key) {
424
+ for (let index = layers.length - 1; index >= 0; index -= 1) {
425
+ const layer = layers[index];
426
+ if (layer?.config.webSearch?.[key] !== undefined)
427
+ return layer.scope;
428
+ }
429
+ return "global";
430
+ }
431
+ /**
432
+ * @param {{ env: NodeJS.ProcessEnv; layers: ConfigLayer[]; mergedConfig: AgentBrowserConfig }} options
433
+ * @returns {Partial<Record<WebSearchProvider, CredentialSource>>}
434
+ */
435
+ export function buildWebSearchCredentialSources(options) {
436
+ /** @type {Partial<Record<WebSearchProvider, CredentialSource>>} */
437
+ const sources = {};
438
+ for (const provider of WEB_SEARCH_PROVIDERS) {
439
+ const descriptor = getWebSearchProviderDescriptor(provider);
440
+ const apiKey = options.mergedConfig.webSearch?.[descriptor.configKey];
441
+ if (apiKey !== undefined) {
442
+ sources[provider] = classifyCredentialSource(apiKey, getWebSearchCredentialScope(options.layers, descriptor.configKey), provider);
443
+ }
444
+ if (!sources[provider] && options.env[descriptor.apiKeyEnv]?.trim()) {
445
+ sources[provider] = { kind: "literal", provider, rawValue: options.env[descriptor.apiKeyEnv] ?? "", scope: "env-fallback" };
446
+ }
447
+ }
448
+ return sources;
449
+ }
450
+ /**
451
+ * @param {{ env: NodeJS.ProcessEnv; layers: ConfigLayer[]; mergedConfig: AgentBrowserConfig; paths: AgentBrowserConfigPaths; errors: string[]; warnings: string[]; projectConfigIncluded?: boolean }} options
452
+ * @returns {AgentBrowserConfigState}
453
+ */
454
+ export function buildAgentBrowserConfigState(options) {
455
+ const webSearchCredentialSources = buildWebSearchCredentialSources(options);
456
+ const trustedBrowserDefaultProfile = getTrustedBrowserDefaultProfile(options.layers);
457
+ const trustedBrowserExecutablePath = getTrustedBrowserExecutablePath(options.layers);
458
+ return {
459
+ browserDefaultProfile: getBrowserDefaultProfile(options.mergedConfig),
460
+ browserDefaultProfileScope: getBrowserDefaultProfileScope(options.layers),
461
+ browserExecutablePath: getBrowserExecutablePath(options.mergedConfig),
462
+ browserExecutablePathScope: getBrowserExecutablePathScope(options.layers),
463
+ trustedBrowserDefaultProfile: trustedBrowserDefaultProfile?.profile,
464
+ trustedBrowserDefaultProfileScope: trustedBrowserDefaultProfile?.scope,
465
+ trustedBrowserExecutablePath: trustedBrowserExecutablePath?.executablePath,
466
+ trustedBrowserExecutablePathScope: trustedBrowserExecutablePath?.scope,
467
+ config: options.mergedConfig,
468
+ webSearchCredentialSources,
469
+ webSearchEnabled: options.mergedConfig.webSearch?.enabled !== false,
470
+ webSearchPreferredProvider: options.mergedConfig.webSearch?.preferredProvider ?? DEFAULT_WEB_SEARCH_PROVIDER,
471
+ errors: options.errors,
472
+ layers: options.layers,
473
+ paths: options.paths,
474
+ projectConfigIncluded: options.projectConfigIncluded ?? options.layers.some((layer) => layer.scope === "project"),
475
+ warnings: options.warnings,
476
+ };
477
+ }
478
+ /**
479
+ * @param {string} path
480
+ * @param {ConfigLayerScope} scope
481
+ * @param {string[]} errors
482
+ * @param {string[]} warnings
483
+ * @returns {ConfigLayer | undefined}
484
+ */
485
+ function readConfigLayerSync(path, scope, errors, warnings) {
486
+ let raw;
487
+ try {
488
+ raw = readFileSync(path, "utf8");
489
+ }
490
+ catch (error) {
491
+ if (error && typeof error === "object" && "code" in error && error.code === "ENOENT")
492
+ return undefined;
493
+ errors.push(`Could not read ${scope} config ${path}: ${error instanceof Error ? error.message : String(error)}`);
494
+ return undefined;
495
+ }
496
+ return parseAgentBrowserConfigLayer(raw, path, scope, errors, warnings);
497
+ }
498
+ /**
499
+ * @param {AgentBrowserConfigLoadOptions} [options]
500
+ * @returns {AgentBrowserConfigState}
501
+ */
502
+ export function loadAgentBrowserConfigStateSync(options = {}) {
503
+ const env = options.env ?? process.env;
504
+ const paths = getAgentBrowserConfigPaths({ cwd: options.cwd, env });
505
+ const includeProjectConfig = options.includeProjectConfig !== false;
506
+ /** @type {string[]} */
507
+ const errors = [];
508
+ /** @type {string[]} */
509
+ const warnings = [];
510
+ /** @type {Array<{ path: string; scope: ConfigLayerScope }>} */
511
+ const layerCandidates = [
512
+ { path: paths.global, scope: "global" },
513
+ ...(includeProjectConfig ? [{ path: paths.project, scope: /** @type {ConfigLayerScope} */ ("project") }] : []),
514
+ ...(paths.override ? [{ path: paths.override, scope: /** @type {ConfigLayerScope} */ ("override") }] : []),
515
+ ];
516
+ /** @type {ConfigLayer[]} */
517
+ const layers = [];
518
+ /** @type {AgentBrowserConfig} */
519
+ let mergedConfig = {};
520
+ for (const candidate of layerCandidates) {
521
+ const layer = readConfigLayerSync(candidate.path, candidate.scope, errors, warnings);
522
+ if (!layer)
523
+ continue;
524
+ layers.push(layer);
525
+ mergedConfig = mergeAgentBrowserConfig(mergedConfig, layer.config);
526
+ }
527
+ return buildAgentBrowserConfigState({
528
+ env,
529
+ errors,
530
+ layers,
531
+ mergedConfig,
532
+ paths,
533
+ projectConfigIncluded: includeProjectConfig,
534
+ warnings,
535
+ });
536
+ }
537
+ /**
538
+ * @param {string} rawValue
539
+ * @param {NodeJS.ProcessEnv} env
540
+ */
541
+ export function resolveEnvInterpolations(rawValue, env) {
542
+ let output = "";
543
+ for (let index = 0; index < rawValue.length; index += 1) {
544
+ const char = rawValue[index];
545
+ if (char !== "$") {
546
+ output += char;
547
+ continue;
548
+ }
549
+ const next = rawValue[index + 1];
550
+ if (next === "$") {
551
+ output += "$";
552
+ index += 1;
553
+ continue;
554
+ }
555
+ if (next === "!") {
556
+ output += "!";
557
+ index += 1;
558
+ continue;
559
+ }
560
+ let name = "";
561
+ if (next === "{") {
562
+ const end = rawValue.indexOf("}", index + 2);
563
+ if (end === -1)
564
+ return undefined;
565
+ name = rawValue.slice(index + 2, end);
566
+ index = end;
567
+ }
568
+ else {
569
+ const match = rawValue.slice(index + 1).match(/^([A-Za-z_][A-Za-z0-9_]*)/);
570
+ if (!match) {
571
+ output += "$";
572
+ continue;
573
+ }
574
+ name = match[1] ?? "";
575
+ index += name.length;
576
+ }
577
+ if (!name)
578
+ return undefined;
579
+ const value = env[name];
580
+ if (value === undefined)
581
+ return undefined;
582
+ output += value;
583
+ }
584
+ return output;
585
+ }
586
+ /**
587
+ * @param {AgentBrowserConfigState} state
588
+ * @param {WebSearchProvider | "auto"} [requestedProvider]
589
+ * @returns {WebSearchProvider[]}
590
+ */
591
+ export function getWebSearchProviderOrder(state, requestedProvider) {
592
+ if (requestedProvider && requestedProvider !== "auto")
593
+ return [requestedProvider];
594
+ const preferred = state.webSearchPreferredProvider;
595
+ return [preferred, ...WEB_SEARCH_PROVIDERS.filter((provider) => provider !== preferred)];
596
+ }
597
+ /**
598
+ * @param {AgentBrowserConfigState} state
599
+ * @param {WebSearchProvider} provider
600
+ */
601
+ export function getWebSearchCredentialSource(state, provider) {
602
+ return state.webSearchCredentialSources[provider];
603
+ }
604
+ /**
605
+ * @param {CredentialSource | undefined} source
606
+ * @param {NodeJS.ProcessEnv} env
607
+ */
608
+ export function hasPotentialCredentialSource(source, env) {
609
+ if (!source)
610
+ return false;
611
+ if (source.kind === "command")
612
+ return true;
613
+ if (source.kind === "env")
614
+ return Boolean(resolveEnvInterpolations(source.rawValue, env)?.trim());
615
+ return Boolean(source.rawValue.trim());
616
+ }
617
+ /**
618
+ * @param {AgentBrowserConfigState} state
619
+ * @param {NodeJS.ProcessEnv} [env]
620
+ */
621
+ export function canRegisterWebSearchTool(state, env = process.env) {
622
+ if (!state.webSearchEnabled || state.errors.length > 0)
623
+ return false;
624
+ return WEB_SEARCH_PROVIDERS.some((provider) => hasPotentialCredentialSource(state.webSearchCredentialSources[provider], env));
625
+ }
626
+ /**
627
+ * @param {CredentialSource | undefined} source
628
+ * @param {WebSearchProvider} [provider]
629
+ */
630
+ export function getCredentialSourceSummary(source, provider) {
631
+ if (!source)
632
+ return "not configured";
633
+ if (source.kind === "command")
634
+ return `configured via command (${source.scope})`;
635
+ if (source.kind === "env")
636
+ return `configured via environment interpolation (${source.scope})`;
637
+ if (source.scope === "env-fallback") {
638
+ return `configured via ${getWebSearchProviderEnvVar(provider ?? source.provider ?? DEFAULT_WEB_SEARCH_PROVIDER)} environment fallback`;
639
+ }
640
+ return `configured as plaintext ${source.scope} value [redacted]`;
641
+ }
642
+ /** @param {AgentBrowserConfigState} state */
643
+ export function formatBrowserProfileStatus(state) {
644
+ const profile = state.browserDefaultProfile;
645
+ if (!profile)
646
+ return "not configured";
647
+ const scope = state.browserDefaultProfileScope ?? "unknown";
648
+ return `${profile.name} (policy: ${profile.policy}; ${scope})`;
649
+ }
650
+ /** @param {AgentBrowserConfigState} state */
651
+ export function formatBrowserExecutableStatus(state) {
652
+ const executablePath = state.browserExecutablePath;
653
+ if (!executablePath)
654
+ return "not configured";
655
+ const scope = state.browserExecutablePathScope ?? "unknown";
656
+ return `${executablePath} (${scope})`;
657
+ }
658
+ /**
659
+ * @param {AgentBrowserConfigState} state
660
+ * @param {(path: string) => boolean} [exists]
661
+ * @returns {ConfigFileSummary[]}
662
+ */
663
+ export function summarizeConfigFiles(state, exists = existsSync) {
664
+ return [
665
+ ["global", state.paths.global],
666
+ ["project", state.paths.project],
667
+ ...(state.paths.override ? [["override", state.paths.override]] : []),
668
+ ].map(([scope, path]) => ({ scope, path, exists: exists(path) }));
669
+ }