@pasko70/pibo 1.14.0 → 1.15.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.
Files changed (25) hide show
  1. package/dist/apps/chat-ui/assets/{dist-DmqwP_tM.js → dist-BFwSuVfD.js} +1 -1
  2. package/dist/apps/chat-ui/assets/{dist-ur4yXrlj.js → dist-BYTi90un.js} +1 -1
  3. package/dist/apps/chat-ui/assets/{dist-hoDORUlL.js → dist-Bi74WdBU.js} +1 -1
  4. package/dist/apps/chat-ui/assets/{dist-BIwXuAMQ.js → dist-CYMM1_bS.js} +1 -1
  5. package/dist/apps/chat-ui/assets/{dist-BG1J2hGI.js → dist-CuIwfDgF.js} +1 -1
  6. package/dist/apps/chat-ui/assets/{dist-LWXXqiGT.js → dist-DS1ScAMf.js} +1 -1
  7. package/dist/apps/chat-ui/assets/{dist-1eQpSfYj.js → dist-DUgrMhHX.js} +1 -1
  8. package/dist/apps/chat-ui/assets/{dist-CtsdsIpj.js → dist-Dq3i1LVZ.js} +1 -1
  9. package/dist/apps/chat-ui/assets/{dist-DfdQQObC.js → dist-Dy5L1xOD.js} +1 -1
  10. package/dist/apps/chat-ui/assets/{dist-BeCHnL7P.js → dist-Zy3KnDp7.js} +1 -1
  11. package/dist/apps/chat-ui/assets/index-BZO4zpMi.js +237 -0
  12. package/dist/apps/chat-ui/index.html +1 -1
  13. package/dist/apps/chat-vscode-web/assets/{index-R_7kgvvD.js → index-V_pKPVAm.js} +3 -3
  14. package/dist/apps/chat-vscode-web/index.html +1 -1
  15. package/dist/apps/vscode-artifacts/latest.vsix +0 -0
  16. package/dist/apps/vscode-artifacts/{pibo-vscode-ext-1.14.0.vsix → pibo-vscode-ext-1.15.0.vsix} +0 -0
  17. package/dist/core/runtime.js +27 -5
  18. package/dist/plugins/builtin.js +7 -0
  19. package/dist/session-ui/terminalRows.js +12 -2
  20. package/dist/tools/browser-use-leases.js +16 -0
  21. package/dist/tools/codex-browser-node-repl.js +224 -0
  22. package/dist/tools/codex-browser-node-worker-source.js +403 -0
  23. package/dist/tools/codex-browser.js +461 -0
  24. package/package.json +3 -1
  25. package/dist/apps/chat-ui/assets/index-DLNTzhdh.js +0 -237
@@ -5,7 +5,7 @@
5
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6
6
  <meta name="theme-color" content="#101d22" />
7
7
  <title>Pibo</title>
8
- <script type="module" crossorigin src="/apps/chat-vscode/assets/index-R_7kgvvD.js"></script>
8
+ <script type="module" crossorigin src="/apps/chat-vscode/assets/index-V_pKPVAm.js"></script>
9
9
  <link rel="stylesheet" crossorigin href="/apps/chat-vscode/assets/index-Dupu1DzQ.css">
10
10
  </head>
11
11
  <body>
@@ -25,6 +25,7 @@ import { registerOpenAiGpt56Models } from "../providers/openai-gpt56.js";
25
25
  import { PIBO_APP_CONTEXT } from "../app-context.js";
26
26
  import { createRuntimeToolDefinition } from "../tools/runtime/tool.js";
27
27
  import { RuntimeSessionRegistry } from "../tools/runtime/registry.js";
28
+ import { CODEX_BROWSER_TOOL_NAMES, CodexBrowserSessionController, createCodexBrowserToolDefinitions, } from "../tools/codex-browser.js";
28
29
  import { compactValidationToolResultForContext } from "./test-output-compaction.js";
29
30
  import { installPiboTranscriptIntegrity } from "./transcript-integrity.js";
30
31
  function hasOwnRetrySetting(settings, key) {
@@ -102,12 +103,18 @@ function getEnabledSkillPaths(cwd, profile) {
102
103
  .filter((skill) => skill.enabled !== false)
103
104
  .map((skill) => resolveProfilePath(cwd, skill.path));
104
105
  }
105
- function getEnabledToolDefinitions(profile, options, subagentRunner, runToolController, runtimeToolController) {
106
+ function getEnabledToolDefinitions(profile, options, subagentRunner, runToolController, runtimeToolController, codexBrowserController) {
106
107
  const runtimeProfileTool = profile.tools.find(isEnabledRuntimeTool);
107
108
  const runtimeTool = runtimeProfileTool && runtimeToolController
108
109
  ? createRuntimeToolDefinition(runtimeToolController)
109
110
  : undefined;
110
- const profileTools = profile.tools.filter((tool) => !isRuntimeTool(tool)).filter(hasEnabledToolDefinition);
111
+ const selectedCodexBrowserToolNames = profile.tools
112
+ .filter(isEnabledCodexBrowserTool)
113
+ .map((tool) => tool.name);
114
+ const codexBrowserTools = codexBrowserController
115
+ ? createCodexBrowserToolDefinitions(codexBrowserController, selectedCodexBrowserToolNames)
116
+ : [];
117
+ const profileTools = profile.tools.filter((tool) => !isRuntimeTool(tool) && !isCodexBrowserTool(tool)).filter(hasEnabledToolDefinition);
111
118
  const profileToolDefinitions = profileTools.map((tool) => getToolDefinition(tool, options.toolContext));
112
119
  const codexCompatEnabled = profile.toolPackages.codexCompat === true;
113
120
  const runControlEnabled = profile.toolPackages.runControl === true;
@@ -129,6 +136,7 @@ function getEnabledToolDefinitions(profile, options, subagentRunner, runToolCont
129
136
  ...(runControlBashTool ? [runControlBashTool] : []),
130
137
  ...profileTools.filter((tool) => tool.yieldable !== false).map((tool) => getToolDefinition(tool, options.toolContext)),
131
138
  ...(runtimeTool && runtimeProfileTool?.yieldable !== false ? [runtimeTool] : []),
139
+ ...codexBrowserTools.filter((definition) => profile.tools.find((tool) => tool.name === definition.name)?.yieldable !== false),
132
140
  ...subagentTools,
133
141
  ...codexCompatTools,
134
142
  ];
@@ -139,6 +147,7 @@ function getEnabledToolDefinitions(profile, options, subagentRunner, runToolCont
139
147
  ...(runControlBashTool ? [runControlBashTool] : []),
140
148
  ...profileToolDefinitions,
141
149
  ...(runtimeTool ? [runtimeTool] : []),
150
+ ...codexBrowserTools,
142
151
  ...subagentTools,
143
152
  ...codexCompatTools,
144
153
  ...goalTools,
@@ -159,6 +168,12 @@ function isRuntimeTool(tool) {
159
168
  function isEnabledRuntimeTool(tool) {
160
169
  return tool.enabled !== false && isRuntimeTool(tool);
161
170
  }
171
+ function isCodexBrowserTool(tool) {
172
+ return tool.builtInPiboTool === "codex_browser" || CODEX_BROWSER_TOOL_NAMES.includes(tool.name);
173
+ }
174
+ function isEnabledCodexBrowserTool(tool) {
175
+ return tool.enabled !== false && isCodexBrowserTool(tool);
176
+ }
162
177
  function isGeneratedPiboTool(name) {
163
178
  return name === "runtime" || name.startsWith("pibo_subagent_") || name.startsWith("pibo_run_") || PIBO_GOAL_TOOL_NAMES.includes(name);
164
179
  }
@@ -280,6 +295,12 @@ export async function createPiboRuntime(options = {}) {
280
295
  const localRuntimeRegistry = ownsLocalRuntimeRegistry ? new RuntimeSessionRegistry({ cwd: runtimeCwd }) : undefined;
281
296
  const runtimeToolController = options.runtimeToolController
282
297
  ?? localRuntimeRegistry?.createController(profile.sessionId ?? "local");
298
+ const codexBrowserController = profile.tools.some(isEnabledCodexBrowserTool)
299
+ ? new CodexBrowserSessionController({
300
+ cwd: runtimeCwd,
301
+ piboSessionId: options.sessionContext?.piboSessionId ?? profile.sessionId ?? runtimeSessionManager.getSessionId(),
302
+ })
303
+ : undefined;
283
304
  const customTools = getEnabledToolDefinitions(profile, {
284
305
  runtimeCwd,
285
306
  shellCommandPrefix: services.settingsManager.getShellCommandPrefix(),
@@ -290,7 +311,7 @@ export async function createPiboRuntime(options = {}) {
290
311
  profileName: profile.profileName,
291
312
  getActiveMessage: options.sessionContext?.getActiveMessage,
292
313
  },
293
- }, options.subagentRunner, options.runToolController, runtimeToolController);
314
+ }, options.subagentRunner, options.runToolController, runtimeToolController, codexBrowserController);
294
315
  const modelDefaults = options.modelDefaults ?? loadPiboModelDefaults(runtimeCwd);
295
316
  const created = await createAgentSessionFromServices({
296
317
  services,
@@ -324,6 +345,7 @@ export async function createPiboRuntime(options = {}) {
324
345
  if (localRuntimeRegistry) {
325
346
  void localRuntimeRegistry.closeControllerSessions(profile.sessionId ?? "local", { force: true });
326
347
  }
348
+ void codexBrowserController?.dispose();
327
349
  originalDispose();
328
350
  };
329
351
  return {
@@ -444,8 +466,8 @@ export async function inspectPiboProfile(options = {}) {
444
466
  })),
445
467
  tools: profile.tools.map((tool) => ({
446
468
  name: tool.name,
447
- hasDefinition: Boolean(tool.definition) || Boolean(tool.createDefinition) || isRuntimeTool(tool),
448
- registered: registeredToolNames.has(tool.name) || tool.providerTool !== undefined || isRuntimeTool(tool),
469
+ hasDefinition: Boolean(tool.definition) || Boolean(tool.createDefinition) || isRuntimeTool(tool) || isCodexBrowserTool(tool),
470
+ registered: registeredToolNames.has(tool.name) || tool.providerTool !== undefined || isRuntimeTool(tool) || isCodexBrowserTool(tool),
449
471
  active: activeToolNames.has(tool.name) || tool.providerTool !== undefined,
450
472
  })).concat(generatedTools),
451
473
  subagents: profile.subagents.map((subagent) => {
@@ -4,6 +4,7 @@ import { createPiboGatewayToolProfiles } from "../gateway/tool.js";
4
4
  import { InitialSessionContextBuilder } from "../core/profiles.js";
5
5
  import { parsePiboThinkingLevel } from "../core/thinking.js";
6
6
  import { createWebSearchToolProfile } from "../tools/web-search.js";
7
+ import { CODEX_BROWSER_TOOL_NAMES, createCodexBrowserToolProfiles } from "../tools/codex-browser.js";
7
8
  import { createRuntimeToolProfile } from "../tools/runtime/tool.js";
8
9
  import { completeLogin, getLoginStatus, removeLogin, setApiKey, startLogin } from "../auth/login-actions.js";
9
10
  import { loadModelCatalog } from "../apps/chat/model-catalog.js";
@@ -166,6 +167,12 @@ export const piboCorePlugin = definePiboPlugin({
166
167
  });
167
168
  api.registerTool(createWebSearchToolProfile());
168
169
  api.registerTool(createRuntimeToolProfile());
170
+ api.registerTools(createCodexBrowserToolProfiles());
171
+ api.registerCapabilityPackage({
172
+ name: "codex-browser-interface",
173
+ description: "Expose Browser Use and its persistent browser-bound Node REPL through a Codex-familiar structured tool surface.",
174
+ toolNames: [...CODEX_BROWSER_TOOL_NAMES],
175
+ });
169
176
  registerPiboNativeTooling(api);
170
177
  api.registerProfile({
171
178
  name: DEFAULT_PIBO_PROFILE_NAME,
@@ -2,6 +2,13 @@ import { compareTraceNodes } from "../shared/trace-engine.js";
2
2
  import { terminalTextValue } from "./terminalValue.js";
3
3
  export const COMPACT_TERMINAL_OUTPUT_PREVIEW_LINES = 5;
4
4
  export const COMPACT_TERMINAL_EXPLORING_PREVIEW_LINES = 6;
5
+ const CODEX_COMPATIBLE_TOOL_LABELS = {
6
+ browser_use_open_tabs: "browser_use.open_tabs",
7
+ browser_use_take_screenshot: "browser_use.take_screenshot",
8
+ browser_use_browser_use: "browser_use.browser_use",
9
+ node_repl_js: "node_repl.js",
10
+ node_repl_js_reset: "node_repl.js_reset",
11
+ };
5
12
  export function buildCompactTerminalRows(traceView, options) {
6
13
  if (!traceView)
7
14
  return [];
@@ -270,7 +277,7 @@ function createToolRowCandidate(node, turnId) {
270
277
  token(toolVerb(node.status), toneForStatus(node.status), "semibold"),
271
278
  token(" "),
272
279
  ],
273
- functionCall: { name: node.title, input: node.input },
280
+ functionCall: { name: displayToolName(node.title), input: node.input },
274
281
  },
275
282
  ...preview.lines,
276
283
  ],
@@ -359,7 +366,7 @@ function createToolResultRow(node) {
359
366
  lines: [
360
367
  {
361
368
  prefix: "bullet",
362
- tokens: [token(node.status === "error" ? "Tool failed" : "Returned", toneForStatus(node.status), "semibold"), token(` ${node.title}`)],
369
+ tokens: [token(node.status === "error" ? "Tool failed" : "Returned", toneForStatus(node.status), "semibold"), token(` ${displayToolName(node.title)}`)],
363
370
  },
364
371
  ...preview.lines,
365
372
  ],
@@ -1030,6 +1037,9 @@ function classifyExploringTool(node) {
1030
1037
  }
1031
1038
  return undefined;
1032
1039
  }
1040
+ function displayToolName(name) {
1041
+ return CODEX_COMPATIBLE_TOOL_LABELS[name] ?? name;
1042
+ }
1033
1043
  function matchesTool(name, terms) {
1034
1044
  return terms.some((term) => name === term || name.startsWith(`${term}_`) || name.endsWith(`_${term}`) || name.includes(term));
1035
1045
  }
@@ -73,6 +73,22 @@ function defaultTemplateDir(status) {
73
73
  // Create an empty template directory so leases get clean profiles.
74
74
  return preferred;
75
75
  }
76
+ export function findActiveBrowserUseLeaseForHolder(status, holder, app, now = new Date()) {
77
+ return readRegistry(status).leases.find((lease) => lease.holder === holder &&
78
+ lease.status === 'active' &&
79
+ !isExpired(lease, now) &&
80
+ (app === undefined || lease.app === app));
81
+ }
82
+ export function browserUseLeaseEnvironment(status, lease) {
83
+ return {
84
+ BROWSER_USE_HOME: browserUseHome(status),
85
+ PIBO_BROWSER_USE_LEASE_ID: lease.id,
86
+ PIBO_BROWSER_USE_SESSION: lease.sessionName,
87
+ PIBO_BROWSER_USE_CHROME_USER_DATA_DIR: lease.userDataDir,
88
+ PIBO_BROWSER_USE_DEFAULT_PROFILE: lease.profileName,
89
+ ...(lease.browserPoolLeaseId ? { PIBO_BROWSER_POOL_LEASE_ID: lease.browserPoolLeaseId } : {}),
90
+ };
91
+ }
76
92
  export function readRegistry(status) {
77
93
  const path = registryPath(status);
78
94
  if (!existsSync(path))
@@ -0,0 +1,224 @@
1
+ import { spawn } from "node:child_process";
2
+ import { createRequire } from "node:module";
3
+ import { createInterface } from "node:readline";
4
+ import { CODEX_BROWSER_NODE_WORKER_SOURCE } from "./codex-browser-node-worker-source.js";
5
+ const packageRequire = createRequire(import.meta.url);
6
+ const ACORN_PATH = packageRequire.resolve("acorn");
7
+ const ACORN_WALK_PATH = packageRequire.resolve("acorn-walk");
8
+ function errorSummary(error, name = "NodeReplError") {
9
+ return error instanceof Error
10
+ ? { name: error.name || name, message: error.message, stack: error.stack }
11
+ : { name, message: String(error) };
12
+ }
13
+ export class CodexBrowserNodeRepl {
14
+ bridge;
15
+ child;
16
+ pending = new Map();
17
+ requestCounter = 0;
18
+ executionCount = 0;
19
+ alive = true;
20
+ busy = false;
21
+ diagnostics = "";
22
+ readyPromise;
23
+ readyResolve;
24
+ readyReject;
25
+ constructor(bridge, cwd) {
26
+ this.bridge = bridge;
27
+ this.readyPromise = new Promise((resolve, reject) => {
28
+ this.readyResolve = resolve;
29
+ this.readyReject = reject;
30
+ });
31
+ this.child = spawn("node", ["-e", CODEX_BROWSER_NODE_WORKER_SOURCE, ACORN_PATH, ACORN_WALK_PATH], {
32
+ cwd,
33
+ env: minimalWorkerEnvironment(),
34
+ stdio: "pipe",
35
+ });
36
+ const stdout = createInterface({ input: this.child.stdout });
37
+ stdout.on("line", (line) => this.handleLine(line));
38
+ this.child.stderr.on("data", (chunk) => {
39
+ this.diagnostics += String(chunk);
40
+ });
41
+ this.child.once("error", (error) => {
42
+ this.alive = false;
43
+ this.readyReject(error);
44
+ this.rejectAll(error);
45
+ });
46
+ this.child.once("close", (code, signal) => {
47
+ this.alive = false;
48
+ const error = new Error(`node_repl worker exited with code ${code ?? "null"}${signal ? ` signal ${signal}` : ""}`);
49
+ this.readyReject(error);
50
+ this.rejectAll(error);
51
+ });
52
+ }
53
+ static async start(bridge, cwd, timeoutMs = 10_000) {
54
+ const repl = new CodexBrowserNodeRepl(bridge, cwd);
55
+ await repl.waitReady(timeoutMs);
56
+ return repl;
57
+ }
58
+ isAlive() {
59
+ return this.alive && !this.child.killed;
60
+ }
61
+ async exec(code, timeoutMs = 30_000) {
62
+ const startedAt = Date.now();
63
+ if (this.busy) {
64
+ return {
65
+ status: "failed",
66
+ stdout: "",
67
+ stderr: "",
68
+ durationMs: 0,
69
+ executionCount: this.executionCount,
70
+ error: { name: "NodeReplBusy", message: "node_repl.js is already executing code." },
71
+ };
72
+ }
73
+ this.busy = true;
74
+ try {
75
+ const response = await this.request("exec", { code, timeoutMs }, timeoutMs + 1_000);
76
+ this.executionCount += 1;
77
+ return {
78
+ status: response.status === "ok" ? "ok" : "error",
79
+ stdout: response.stdout ?? "",
80
+ stderr: response.stderr ?? "",
81
+ result: response.result,
82
+ error: response.error,
83
+ durationMs: Date.now() - startedAt,
84
+ executionCount: this.executionCount,
85
+ };
86
+ }
87
+ catch (error) {
88
+ return {
89
+ status: error instanceof NodeReplTimeoutError ? "timeout" : "failed",
90
+ stdout: "",
91
+ stderr: this.diagnostics,
92
+ durationMs: Date.now() - startedAt,
93
+ executionCount: this.executionCount,
94
+ error: errorSummary(error),
95
+ };
96
+ }
97
+ finally {
98
+ this.busy = false;
99
+ }
100
+ }
101
+ async reset(timeoutMs = 10_000) {
102
+ if (this.busy) {
103
+ return {
104
+ status: "failed",
105
+ reset: false,
106
+ error: { name: "NodeReplBusy", message: "node_repl.js cannot reset while code is executing." },
107
+ };
108
+ }
109
+ try {
110
+ const response = await this.request("reset", {}, timeoutMs);
111
+ this.executionCount = 0;
112
+ return { status: response.status === "ok" ? "ok" : "failed", reset: response.reset === true, error: response.error };
113
+ }
114
+ catch (error) {
115
+ return { status: "failed", reset: false, error: errorSummary(error) };
116
+ }
117
+ }
118
+ dispose() {
119
+ if (!this.isAlive())
120
+ return;
121
+ this.alive = false;
122
+ this.child.kill("SIGTERM");
123
+ this.rejectAll(new Error("node_repl worker was disposed"));
124
+ }
125
+ waitReady(timeoutMs) {
126
+ return new Promise((resolve, reject) => {
127
+ const timer = setTimeout(() => reject(new Error(`Timed out waiting for node_repl worker. ${this.diagnostics}`)), timeoutMs);
128
+ this.readyPromise.then(() => {
129
+ clearTimeout(timer);
130
+ resolve();
131
+ }, (error) => {
132
+ clearTimeout(timer);
133
+ reject(error);
134
+ });
135
+ });
136
+ }
137
+ request(type, payload, timeoutMs) {
138
+ if (!this.isAlive())
139
+ return Promise.reject(new Error("node_repl worker is not alive"));
140
+ const id = `request_${++this.requestCounter}`;
141
+ return new Promise((resolve, reject) => {
142
+ const timer = setTimeout(() => {
143
+ this.pending.delete(id);
144
+ reject(new NodeReplTimeoutError(`node_repl request ${type} timed out after ${timeoutMs}ms`));
145
+ }, timeoutMs);
146
+ this.pending.set(id, { resolve, reject, timer });
147
+ this.write({ type, id, ...payload });
148
+ });
149
+ }
150
+ handleLine(line) {
151
+ let message;
152
+ try {
153
+ message = JSON.parse(line);
154
+ }
155
+ catch {
156
+ this.alive = false;
157
+ this.rejectAll(new Error(`Invalid node_repl worker protocol line: ${line}`));
158
+ this.child.kill("SIGTERM");
159
+ return;
160
+ }
161
+ if (message.type === "ready" && message.status === "ready") {
162
+ this.readyResolve();
163
+ return;
164
+ }
165
+ if (message.type === "browser_request") {
166
+ void this.handleBrowserRequest(message);
167
+ return;
168
+ }
169
+ const id = message.id;
170
+ if (!id)
171
+ return;
172
+ const pending = this.pending.get(id);
173
+ if (!pending)
174
+ return;
175
+ this.pending.delete(id);
176
+ clearTimeout(pending.timer);
177
+ pending.resolve(message);
178
+ }
179
+ async handleBrowserRequest(message) {
180
+ if (!message.id)
181
+ return;
182
+ try {
183
+ let result;
184
+ if (message.operation === "open_tabs") {
185
+ result = await this.bridge.openTabs();
186
+ }
187
+ else if (message.operation === "use") {
188
+ if (!message.input || typeof message.input !== "object" || Array.isArray(message.input)) {
189
+ throw new Error("browser.use requires an action object");
190
+ }
191
+ result = await this.bridge.use(message.input);
192
+ }
193
+ else {
194
+ throw new Error(`Unknown browser bridge operation ${message.operation ?? ""}`);
195
+ }
196
+ this.write({ type: "browser_response", id: message.id, result });
197
+ }
198
+ catch (error) {
199
+ this.write({ type: "browser_response", id: message.id, error: errorSummary(error, "BrowserUseError") });
200
+ }
201
+ }
202
+ write(message) {
203
+ if (!this.isAlive())
204
+ return;
205
+ this.child.stdin.write(`${JSON.stringify(message)}\n`);
206
+ }
207
+ rejectAll(error) {
208
+ for (const [id, pending] of this.pending) {
209
+ this.pending.delete(id);
210
+ clearTimeout(pending.timer);
211
+ pending.reject(error);
212
+ }
213
+ }
214
+ }
215
+ function minimalWorkerEnvironment() {
216
+ const env = {};
217
+ for (const name of ["PATH", "HOME", "TMPDIR", "TEMP", "TMP", "LANG", "LC_ALL", "TZ"]) {
218
+ if (process.env[name] !== undefined)
219
+ env[name] = process.env[name];
220
+ }
221
+ return env;
222
+ }
223
+ class NodeReplTimeoutError extends Error {
224
+ }