@pasko70/pibo 3.2.2 → 3.2.4
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/dist/agent-runtime/auth.js +3 -5
- package/dist/agent-runtime/portable-history.js +1 -1
- package/dist/agent-runtimes/codex-native/client.js +2 -5
- package/dist/agent-runtimes/codex-native/redaction.js +3 -27
- package/dist/agent-runtimes/omp/client.js +2 -4
- package/dist/agent-runtimes/pi/adapter.js +1 -0
- package/dist/agent-runtimes/pi/runtime.js +1 -1
- package/dist/apps/chat/agent-store.js +1 -1
- package/dist/apps/chat/chat-files.js +3 -1
- package/dist/apps/chat/chat-request-normalizers.js +1 -1
- package/dist/apps/chat/chat-user-skill-routes.js +7 -0
- package/dist/apps/chat/data/project-service.js +49 -1
- package/dist/apps/chat/data/session-query-service.js +46 -6
- package/dist/apps/chat/loop-api.js +10 -1
- package/dist/apps/chat/ralph-api.js +10 -1
- package/dist/apps/chat/web-app.js +22 -5
- package/dist/apps/chat-ui/assets/{dist-f-76_fmW.js → dist-CTC-_3l0.js} +1 -1
- package/dist/apps/chat-ui/assets/{dist-DI39iyXK.js → dist-CftLEFd5.js} +1 -1
- package/dist/apps/chat-ui/assets/{dist-BL4oTUrk.js → dist-Dq27Wt2q.js} +1 -1
- package/dist/apps/chat-ui/assets/{dist-Drv_xdGh.js → dist-Y4fHV5-Z.js} +1 -1
- package/dist/apps/chat-ui/assets/{dist-CQF9jyQW.js → dist-dGF-PDGY.js} +1 -1
- package/dist/apps/chat-ui/assets/index-C_P0gXvJ.js +228 -0
- package/dist/apps/chat-ui/assets/index-DUGVw259.css +1 -0
- package/dist/apps/chat-ui/index.html +2 -2
- package/dist/apps/chat-vscode-web/assets/index-B5X0NaRo.js +43 -0
- package/dist/apps/chat-vscode-web/index.html +1 -1
- package/dist/apps/cli-ui/InkSessionApp.js +38 -28
- package/dist/auth/cli.js +4 -6
- package/dist/auth/machine-keys.js +20 -3
- package/dist/cli-session/localSessionSource.js +2 -4
- package/dist/compute/cli.js +16 -13
- package/dist/compute/docker.js +96 -48
- package/dist/core/base-prompt.js +27 -5
- package/dist/core/context-build.js +5 -15
- package/dist/core/sensitive-data-redaction.js +2 -2
- package/dist/core/session-router.js +15 -4
- package/dist/cron/cli.js +42 -10
- package/dist/cron/store.js +1 -1
- package/dist/data/cli.js +15 -7
- package/dist/data/payload-store.js +38 -0
- package/dist/data/schema.js +9 -1
- package/dist/debug/index.js +5 -0
- package/dist/debug/pty.js +45 -10
- package/dist/gateway/cli.js +18 -2
- package/dist/gateway/server.js +1 -0
- package/dist/loops/service.js +32 -16
- package/dist/mcp/runtime-session.js +2 -1
- package/dist/pi-packages/cli.js +5 -0
- package/dist/previews/cli.js +2 -6
- package/dist/previews/config.js +10 -0
- package/dist/previews/manager.js +2 -9
- package/dist/ralph/service.js +21 -10
- package/dist/session-ui/statusViewModel.js +2 -4
- package/dist/sessions/pibo-data-store.js +18 -0
- package/dist/sessions/store.js +9 -0
- package/dist/setup/cli.js +14 -7
- package/dist/setup/installation-profiles.js +20 -7
- package/dist/shared/trace-event-projection.js +32 -1
- package/dist/subagents/tool.js +3 -3
- package/dist/tools/browser-use-leases.js +11 -3
- package/dist/tools/index.js +0 -1
- package/dist/tools/runtime/node-worker-source.js +39 -5
- package/dist/tools/runtime/python-backend.js +6 -2
- package/dist/tools/runtime/registry.js +38 -6
- package/dist/vscode/cli.js +1 -2
- package/dist/vscode/install.js +21 -14
- package/npm-shrinkwrap.json +2 -2
- package/package.json +3 -1
- package/dist/apps/chat-ui/assets/index-BTzIdlcK.css +0 -1
- package/dist/apps/chat-ui/assets/index-CgFDCgyv.js +0 -228
- package/dist/apps/chat-vscode-web/assets/index-B-TiIWb7.js +0 -43
package/dist/previews/manager.js
CHANGED
|
@@ -4,8 +4,7 @@ import { existsSync, readdirSync, readFileSync } from "node:fs";
|
|
|
4
4
|
import { fileURLToPath } from "node:url";
|
|
5
5
|
import { basename } from "node:path";
|
|
6
6
|
import { promisify } from "node:util";
|
|
7
|
-
import {
|
|
8
|
-
import { loadPreviewConfig } from "./config.js";
|
|
7
|
+
import { loadEffectivePreviewServerSettings } from "./config.js";
|
|
9
8
|
import { findPreviewTargetProcess, isPreviewTargetProcessCurrent, previewProcessStartTicks, probePreviewTarget, } from "./network.js";
|
|
10
9
|
import { previewExposureState } from "./store.js";
|
|
11
10
|
const execFileAsync = promisify(execFile);
|
|
@@ -25,13 +24,7 @@ export function validatePreviewStartCommand(value) {
|
|
|
25
24
|
}
|
|
26
25
|
export async function startManagedPreview(store, id, options = {}) {
|
|
27
26
|
const controller = options.controller ?? createDefaultPreviewProcessController();
|
|
28
|
-
const settings = options.settings ?? (
|
|
29
|
-
const previewConfig = loadPreviewConfig();
|
|
30
|
-
return sanitizePreviewServerSettings({
|
|
31
|
-
maxRunningServers: previewConfig.maxRunningServers,
|
|
32
|
-
autoStopMinutes: previewConfig.autoStopMinutes,
|
|
33
|
-
});
|
|
34
|
-
})();
|
|
27
|
+
const settings = options.settings ?? loadEffectivePreviewServerSettings();
|
|
35
28
|
const now = options.now?.() ?? new Date();
|
|
36
29
|
await reconcileManagedPreviews(store, { ...options, controller, settings, now: () => now });
|
|
37
30
|
let current = store.requireExposure(id);
|
package/dist/ralph/service.js
CHANGED
|
@@ -10,6 +10,9 @@ import { createDefaultPiboRalphStore } from './store.js';
|
|
|
10
10
|
import { createBuiltInRalphStopConditions, evaluateRalphStopPolicy } from './stopping.js';
|
|
11
11
|
const CHAT_WEB_CHANNEL = 'pibo.chat-web';
|
|
12
12
|
function errorMessage(error) { return error instanceof Error ? error.message : String(error); }
|
|
13
|
+
export class PiboRalphCapacityError extends Error {
|
|
14
|
+
constructor() { super('Ralph run capacity is currently full'); this.name = 'PiboRalphCapacityError'; }
|
|
15
|
+
}
|
|
13
16
|
class RalphRunTimeoutError extends Error {
|
|
14
17
|
abortFailed;
|
|
15
18
|
constructor(message, abortFailed = false) {
|
|
@@ -46,6 +49,7 @@ export class PiboRalphService {
|
|
|
46
49
|
runTimeoutMs;
|
|
47
50
|
timer;
|
|
48
51
|
activeRuns = 0;
|
|
52
|
+
admissionTail = Promise.resolve();
|
|
49
53
|
stopped = true;
|
|
50
54
|
cancelledRuns = new Set();
|
|
51
55
|
unsubscribeProductEvents;
|
|
@@ -63,9 +67,11 @@ export class PiboRalphService {
|
|
|
63
67
|
stop() { this.stopped = true; if (this.timer)
|
|
64
68
|
clearTimeout(this.timer); this.timer = undefined; this.unsubscribeProductEvents?.(); this.unsubscribeProductEvents = undefined; this.dataStore.close(); this.store.close(); }
|
|
65
69
|
status() { return { enabled: !this.stopped, ...this.store.status() }; }
|
|
66
|
-
async startJob(id) { const
|
|
70
|
+
async startJob(id) { return await this.withAdmissionLock(async () => { const existing = this.store.getJob(id); if (!existing || existing.state.runningAt)
|
|
71
|
+
return undefined; if (this.activeRuns >= this.maxConcurrentRuns)
|
|
72
|
+
throw new PiboRalphCapacityError(); const job = this.store.updateJob(id, { enabled: true }); if (!job)
|
|
67
73
|
return undefined; const reserved = await this.reserveAfterBeforeRunEvaluation(job); if (!reserved)
|
|
68
|
-
return undefined; void this.executeReserved(reserved.job, reserved.run).finally(() => this.armSoon()); return reserved.run; }
|
|
74
|
+
return undefined; void this.executeReserved(reserved.job, reserved.run).finally(() => this.armSoon()); return reserved.run; }); }
|
|
69
75
|
stopJob(id) { const job = this.store.requestStop(id); this.armSoon(); return job; }
|
|
70
76
|
async cancelJob(id) {
|
|
71
77
|
const job = this.store.requestCancel(id);
|
|
@@ -82,14 +88,13 @@ export class PiboRalphService {
|
|
|
82
88
|
async tick() { if (this.stopped)
|
|
83
89
|
return; try {
|
|
84
90
|
await this.abortCancelRequestedJobs();
|
|
85
|
-
const
|
|
86
|
-
|
|
87
|
-
const
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
}
|
|
91
|
+
const jobs = this.store.listJobs({ includeDisabled: false }).filter((job) => !job.state.runningAt);
|
|
92
|
+
for (const job of jobs) {
|
|
93
|
+
const admitted = await this.withAdmissionLock(async () => { if (this.activeRuns >= this.maxConcurrentRuns)
|
|
94
|
+
return false; const reserved = await this.reserveAfterBeforeRunEvaluation(job); if (!reserved)
|
|
95
|
+
return true; void this.executeReserved(reserved.job, reserved.run).finally(() => this.armSoon()); return true; });
|
|
96
|
+
if (!admitted)
|
|
97
|
+
break;
|
|
93
98
|
}
|
|
94
99
|
}
|
|
95
100
|
catch (error) {
|
|
@@ -98,6 +103,12 @@ export class PiboRalphService {
|
|
|
98
103
|
finally {
|
|
99
104
|
this.arm();
|
|
100
105
|
} }
|
|
106
|
+
async withAdmissionLock(action) { const previous = this.admissionTail; let release; this.admissionTail = new Promise((resolve) => { release = resolve; }); await previous; try {
|
|
107
|
+
return await action();
|
|
108
|
+
}
|
|
109
|
+
finally {
|
|
110
|
+
release();
|
|
111
|
+
} }
|
|
101
112
|
async reserveAfterBeforeRunEvaluation(job) {
|
|
102
113
|
const fresh = this.store.getJob(job.id) ?? job;
|
|
103
114
|
if (!fresh.enabled || fresh.state.runningAt)
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { redactSensitiveText } from "../core/sensitive-data-redaction.js";
|
|
1
2
|
export function buildTerminalStatusViewModel(input = {}) {
|
|
2
3
|
const fields = [];
|
|
3
4
|
const progress = [];
|
|
@@ -56,10 +57,7 @@ export function progressBarText(descriptor, width = 20) {
|
|
|
56
57
|
return `${"█".repeat(filled)}${"░".repeat(boundedWidth - filled)} ${descriptor.percent.toFixed(1)}%`;
|
|
57
58
|
}
|
|
58
59
|
export function redactTerminalSecret(text) {
|
|
59
|
-
return text
|
|
60
|
-
.replace(/\b([A-Z0-9_]*(?:API[_-]?KEY|TOKEN|SECRET|PASSWORD)[A-Z0-9_]*)\s*=\s*([^\s]+)/gi, "$1=[redacted]")
|
|
61
|
-
.replace(/\b(api[_-]?key|token|secret|password)\s*[:=]\s*([^\s]+)/gi, "$1=[redacted]")
|
|
62
|
-
.replace(/\b(?:sk|pk|pibo|ghp|github_pat)_[A-Za-z0-9_\-]{8,}\b/g, "[redacted]");
|
|
60
|
+
return redactSensitiveText(text).replace(/\b([A-Z0-9_]*(?:API[_-]?KEY|TOKEN|SECRET|PASSWORD|PASSWD)[A-Z0-9_]*)\s*:\s*\[redacted\]/gi, "$1=[redacted]");
|
|
63
61
|
}
|
|
64
62
|
function contextProgress(usage) {
|
|
65
63
|
if (!usage || (usage.tokens === undefined && usage.contextWindow === undefined && usage.percent === undefined)) {
|
|
@@ -147,6 +147,24 @@ export class PiboDataSessionStore {
|
|
|
147
147
|
`).run(sequence, new Date().toISOString(), piboSessionId);
|
|
148
148
|
});
|
|
149
149
|
}
|
|
150
|
+
claimAgentObservationSequence(parentPiboSessionId, minimum) {
|
|
151
|
+
return this.dataStore.transaction(() => {
|
|
152
|
+
const session = this.db.prepare("SELECT 1 AS present FROM sessions WHERE id = ? AND deleted_at IS NULL").get(parentPiboSessionId);
|
|
153
|
+
if (!session)
|
|
154
|
+
return minimum;
|
|
155
|
+
const row = this.db.prepare("SELECT next_sequence FROM session_agent_observation_counters WHERE parent_pibo_session_id = ?").get(parentPiboSessionId);
|
|
156
|
+
const migrationSafeSeed = Math.trunc(Date.now()) * 1_000;
|
|
157
|
+
const sequence = Math.max(minimum, row?.next_sequence ?? migrationSafeSeed);
|
|
158
|
+
this.db.prepare(`
|
|
159
|
+
INSERT INTO session_agent_observation_counters (parent_pibo_session_id, next_sequence, updated_at)
|
|
160
|
+
VALUES (?, ?, ?)
|
|
161
|
+
ON CONFLICT(parent_pibo_session_id) DO UPDATE SET
|
|
162
|
+
next_sequence = excluded.next_sequence,
|
|
163
|
+
updated_at = excluded.updated_at
|
|
164
|
+
`).run(parentPiboSessionId, sequence + 1, new Date().toISOString());
|
|
165
|
+
return sequence;
|
|
166
|
+
});
|
|
167
|
+
}
|
|
150
168
|
claimOrAttachOutputPart(input) {
|
|
151
169
|
return this.dataStore.transaction(() => {
|
|
152
170
|
const indexAttribute = outputPartIndexAttribute(input.kind);
|
package/dist/sessions/store.js
CHANGED
|
@@ -49,6 +49,7 @@ export class InMemoryPiboSessionStore {
|
|
|
49
49
|
byPiSessionId = new Map();
|
|
50
50
|
byNativeSession = new Map();
|
|
51
51
|
outputRenderHighWater = new Map();
|
|
52
|
+
agentObservationNextSequence = new Map();
|
|
52
53
|
outputToolInvocationNextOrdinal = new Map();
|
|
53
54
|
get(id) {
|
|
54
55
|
return this.byId.get(id);
|
|
@@ -120,6 +121,13 @@ export class InMemoryPiboSessionStore {
|
|
|
120
121
|
if (sequence > current)
|
|
121
122
|
this.outputRenderHighWater.set(piboSessionId, sequence);
|
|
122
123
|
}
|
|
124
|
+
claimAgentObservationSequence(parentPiboSessionId, minimum) {
|
|
125
|
+
if (!this.byId.has(parentPiboSessionId))
|
|
126
|
+
return minimum;
|
|
127
|
+
const sequence = Math.max(minimum, this.agentObservationNextSequence.get(parentPiboSessionId) ?? 1);
|
|
128
|
+
this.agentObservationNextSequence.set(parentPiboSessionId, sequence + 1);
|
|
129
|
+
return sequence;
|
|
130
|
+
}
|
|
123
131
|
claimOutputToolInvocationOrdinal(piboSessionId, eventId, toolCallId) {
|
|
124
132
|
const key = outputToolInvocationCounterKey(piboSessionId, eventId, toolCallId);
|
|
125
133
|
const ordinal = this.outputToolInvocationNextOrdinal.get(key) ?? 0;
|
|
@@ -163,6 +171,7 @@ export class InMemoryPiboSessionStore {
|
|
|
163
171
|
if (nativeKey)
|
|
164
172
|
this.byNativeSession.delete(nativeKey);
|
|
165
173
|
this.outputRenderHighWater.delete(id);
|
|
174
|
+
this.agentObservationNextSequence.delete(id);
|
|
166
175
|
const counterPrefix = `${JSON.stringify([id]).slice(0, -1)},`;
|
|
167
176
|
for (const key of this.outputToolInvocationNextOrdinal.keys()) {
|
|
168
177
|
if (key.startsWith(counterPrefix))
|
package/dist/setup/cli.js
CHANGED
|
@@ -7,7 +7,7 @@ import { Command } from "commander";
|
|
|
7
7
|
import { getPiboConfigValue, loadPiboConfig } from "../config/config.js";
|
|
8
8
|
import { getPiboHome } from "../core/pibo-home.js";
|
|
9
9
|
import { getWslInfo, isWsl } from "../core/wsl.js";
|
|
10
|
-
import { createInstallationPlan, inspectInstallation, installationManifestPath, materializeInstallationPlan, parseInstallationComponent, parseInstallationProfile, readInstallationManifest, runPendingInstallationActions, uninstallInstallation, validateInstallationPlanTargets, } from "./installation-profiles.js";
|
|
10
|
+
import { createInstallationPlan, inspectInstallation, installationManifestPath, installationOutputPath, materializeInstallationPlan, parseInstallationComponent, parseInstallationProfile, readInstallationManifest, runPendingInstallationActions, uninstallInstallation, validateInstallationPlanTargets, } from "./installation-profiles.js";
|
|
11
11
|
function parsePort(value) {
|
|
12
12
|
const port = Number(value);
|
|
13
13
|
if (!Number.isInteger(port) || port < 1 || port > 65535)
|
|
@@ -126,7 +126,7 @@ export function createUserHostSetupPlan(options = {}) {
|
|
|
126
126
|
const wwwDomain = options.wwwDomain ?? (options.domain ? `www.${options.domain}` : undefined);
|
|
127
127
|
const warnings = [];
|
|
128
128
|
if (!options.domain)
|
|
129
|
-
warnings.push("No production domain was provided;
|
|
129
|
+
warnings.push("No production domain was provided; the Caddyfile is omitted and Auth examples use placeholders.");
|
|
130
130
|
if (process.platform === "win32" && !isWsl()) {
|
|
131
131
|
warnings.push("Pibo host setup targets Linux. Native Windows is not supported. Install WSL2 (https://aka.ms/wsl) and run setup inside the WSL distribution. See docs/project/guides/pibo-on-windows-via-wsl.md.");
|
|
132
132
|
}
|
|
@@ -149,7 +149,14 @@ export function createUserHostSetupPlan(options = {}) {
|
|
|
149
149
|
content: userEnvTemplate({ domain: options.domain, piboHome }),
|
|
150
150
|
},
|
|
151
151
|
];
|
|
152
|
-
if (
|
|
152
|
+
if (serviceName !== "pibo-web") {
|
|
153
|
+
generatedFiles.push({
|
|
154
|
+
path: `${piboHome}/gateway-web-service`,
|
|
155
|
+
purpose: "Managed production gateway service identity",
|
|
156
|
+
content: `${serviceName}\n`,
|
|
157
|
+
});
|
|
158
|
+
}
|
|
159
|
+
if (options.includeCaddy !== false && options.domain) {
|
|
153
160
|
generatedFiles.push({
|
|
154
161
|
path: "/etc/caddy/Caddyfile",
|
|
155
162
|
purpose: "HTTPS reverse proxy for the production gateway",
|
|
@@ -213,7 +220,7 @@ export function createDeveloperHostSetupPlan(options = {}) {
|
|
|
213
220
|
piboHome: prodHome,
|
|
214
221
|
serviceKind: "prod",
|
|
215
222
|
webPort: prodWebPort,
|
|
216
|
-
execStart: `${prodEntrypoint} gateway:web --web-host 127.0.0.1 --web-port ${prodWebPort}`,
|
|
223
|
+
execStart: `${prodEntrypoint} gateway:web --web-host 127.0.0.1 --web-port ${prodWebPort} --gateway-port ${prodGatewayPort}`,
|
|
217
224
|
}),
|
|
218
225
|
},
|
|
219
226
|
{
|
|
@@ -871,7 +878,7 @@ export async function runSetupCli(argv = process.argv) {
|
|
|
871
878
|
.action((options) => {
|
|
872
879
|
if (options.root && options.apply)
|
|
873
880
|
throw new Error("Use either --root for staging or --apply for the host, not both");
|
|
874
|
-
const manifestPath =
|
|
881
|
+
const manifestPath = installationOutputPath(installationManifestPath(options.piboHome), options.root);
|
|
875
882
|
const manifest = readInstallationManifest(manifestPath);
|
|
876
883
|
if (!manifest)
|
|
877
884
|
throw new Error(`No installation manifest found at ${manifestPath}`);
|
|
@@ -893,7 +900,7 @@ export async function runSetupCli(argv = process.argv) {
|
|
|
893
900
|
.action((name, options) => {
|
|
894
901
|
if (options.root && options.apply)
|
|
895
902
|
throw new Error("Use either --root for staging or --apply for the host, not both");
|
|
896
|
-
const manifestPath =
|
|
903
|
+
const manifestPath = installationOutputPath(installationManifestPath(options.piboHome), options.root);
|
|
897
904
|
const manifest = readInstallationManifest(manifestPath);
|
|
898
905
|
if (!manifest)
|
|
899
906
|
throw new Error(`No installation manifest found at ${manifestPath}`);
|
|
@@ -915,7 +922,7 @@ export async function runSetupCli(argv = process.argv) {
|
|
|
915
922
|
.action((options) => {
|
|
916
923
|
if (options.root && options.apply)
|
|
917
924
|
throw new Error("Use either --root for staging or --apply for the host, not both");
|
|
918
|
-
const manifestPath =
|
|
925
|
+
const manifestPath = installationOutputPath(installationManifestPath(options.piboHome), options.root);
|
|
919
926
|
const manifest = readInstallationManifest(manifestPath);
|
|
920
927
|
if (!manifest)
|
|
921
928
|
throw new Error(`No installation manifest found at ${manifestPath}`);
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { createHash } from "node:crypto";
|
|
2
2
|
import { chmodSync, existsSync, mkdirSync, readFileSync, renameSync, rmSync, statSync, writeFileSync } from "node:fs";
|
|
3
|
-
import { dirname, isAbsolute, join } from "node:path";
|
|
3
|
+
import { dirname, isAbsolute, join, parse, relative, resolve, sep } from "node:path";
|
|
4
4
|
const CODE_SERVER_VERSION = "4.135.0";
|
|
5
5
|
const BROWSER_USE_VERSION = "0.12.6";
|
|
6
6
|
const AGENT_BROWSER_VERSION = "0.27.0";
|
|
@@ -28,6 +28,9 @@ function sha256(content) {
|
|
|
28
28
|
function shellQuote(value) {
|
|
29
29
|
return `'${value.replace(/'/g, `'\\''`)}'`;
|
|
30
30
|
}
|
|
31
|
+
function systemdQuote(value) {
|
|
32
|
+
return `"${value.replace(/\\/g, "\\\\").replace(/"/g, '\\"')}"`;
|
|
33
|
+
}
|
|
31
34
|
function normalizeContent(content) {
|
|
32
35
|
return content.endsWith("\n") ? content : `${content}\n`;
|
|
33
36
|
}
|
|
@@ -36,8 +39,8 @@ export function installationManifestPath(piboHome) {
|
|
|
36
39
|
}
|
|
37
40
|
function gatewayService(options) {
|
|
38
41
|
const integrationEnvironment = [
|
|
39
|
-
...(options.hasVscode ? ["Environment=PIBO_VSCODE_WEB_URL=/apps/vscode/", `Environment
|
|
40
|
-
...(options.hasMcpDefaults ? [`Environment
|
|
42
|
+
...(options.hasVscode ? ["Environment=PIBO_VSCODE_WEB_URL=/apps/vscode/", `Environment=${systemdQuote(`PIBO_VSCODE_WORKSPACE_ROOT=${options.workspaceRoot}`)}`] : []),
|
|
43
|
+
...(options.hasMcpDefaults ? [`Environment=${systemdQuote(`MCP_CONFIG_PATH=${options.piboHome}/setup/mcp-defaults.json`)}`] : []),
|
|
41
44
|
];
|
|
42
45
|
return `[Unit]
|
|
43
46
|
Description=Pibo web gateway
|
|
@@ -49,7 +52,7 @@ Type=simple
|
|
|
49
52
|
User=root
|
|
50
53
|
WorkingDirectory=/root
|
|
51
54
|
Environment=HOME=/root
|
|
52
|
-
Environment
|
|
55
|
+
Environment=${systemdQuote(`PIBO_HOME=${options.piboHome}`)}
|
|
53
56
|
Environment=NODE_ENV=production
|
|
54
57
|
Environment=PIBO_GATEWAY_WEB_PORT=4788
|
|
55
58
|
${integrationEnvironment.length > 0 ? `${integrationEnvironment.join("\n")}\n` : ""}ExecStart=${options.piboCommand} gateway:web --web-host 127.0.0.1 --web-port 4788
|
|
@@ -76,14 +79,14 @@ WorkingDirectory=${workspaceRoot}
|
|
|
76
79
|
Environment=HOME=/var/lib/pibo-code
|
|
77
80
|
Environment=XDG_DATA_HOME=/var/lib/pibo-code/.local/share
|
|
78
81
|
Environment=XDG_CONFIG_HOME=/var/lib/pibo-code/.config
|
|
79
|
-
ExecStart=/opt/pibo/code-server/${CODE_SERVER_VERSION}/bin/code-server --bind-addr 127.0.0.1:4790 --auth none --disable-telemetry --disable-update-check --disable-workspace-trust ${workspaceRoot}
|
|
82
|
+
ExecStart=/opt/pibo/code-server/${CODE_SERVER_VERSION}/bin/code-server --bind-addr 127.0.0.1:4790 --auth none --disable-telemetry --disable-update-check --disable-workspace-trust ${systemdQuote(workspaceRoot)}
|
|
80
83
|
Restart=always
|
|
81
84
|
RestartSec=5
|
|
82
85
|
NoNewPrivileges=true
|
|
83
86
|
PrivateTmp=true
|
|
84
87
|
ProtectSystem=strict
|
|
85
88
|
ProtectHome=read-only
|
|
86
|
-
ReadWritePaths=${workspaceRoot} /var/lib/pibo-code
|
|
89
|
+
ReadWritePaths=${systemdQuote(workspaceRoot)} /var/lib/pibo-code
|
|
87
90
|
|
|
88
91
|
[Install]
|
|
89
92
|
WantedBy=multi-user.target
|
|
@@ -254,7 +257,17 @@ function ownedDirectoryMarkerParent(path) {
|
|
|
254
257
|
function outputPath(path, root) {
|
|
255
258
|
if (!root)
|
|
256
259
|
return path;
|
|
257
|
-
|
|
260
|
+
const resolvedRoot = resolve(root);
|
|
261
|
+
const stagedPath = isAbsolute(path) ? relative(parse(path).root, path) : path;
|
|
262
|
+
const destination = resolve(resolvedRoot, stagedPath);
|
|
263
|
+
const relativeDestination = relative(resolvedRoot, destination);
|
|
264
|
+
if (relativeDestination === ".." || relativeDestination.startsWith(`..${sep}`) || isAbsolute(relativeDestination)) {
|
|
265
|
+
throw new Error(`Refusing staged path outside root: ${path}`);
|
|
266
|
+
}
|
|
267
|
+
return destination;
|
|
268
|
+
}
|
|
269
|
+
export function installationOutputPath(path, root) {
|
|
270
|
+
return outputPath(path, root);
|
|
258
271
|
}
|
|
259
272
|
function atomicWrite(path, content, mode) {
|
|
260
273
|
mkdirSync(dirname(path), { recursive: true });
|
|
@@ -55,6 +55,9 @@ export function applySingleEventToNodes(nodes, byId, piboSessionId, storedEvent,
|
|
|
55
55
|
if (!node)
|
|
56
56
|
return;
|
|
57
57
|
applyStoredPayloadRef(node, payload, storedEvent.storedPayloadRef);
|
|
58
|
+
if (payload.type === "session_error" && payload.eventId) {
|
|
59
|
+
node.parentId = settleTurnFromSessionError(byId, payload.eventId, payload, storedEvent.createdAt);
|
|
60
|
+
}
|
|
58
61
|
if (node.type === "user.message" &&
|
|
59
62
|
node.status === "running" &&
|
|
60
63
|
!node.parentId &&
|
|
@@ -280,10 +283,11 @@ export function markIncompletePersistedTurns(nodes, byId, piboSessionId, events,
|
|
|
280
283
|
turn.status = "error";
|
|
281
284
|
turn.summary = INCOMPLETE_TURN_SUMMARY;
|
|
282
285
|
turn.error = INCOMPLETE_TURN_ERROR;
|
|
286
|
+
const last = lastByEventId.get(eventId) ?? start;
|
|
287
|
+
settleInterruptedTurnChildren(byId, turnId, INCOMPLETE_TURN_ERROR);
|
|
283
288
|
const markerId = `event:incomplete-turn:${eventId}`;
|
|
284
289
|
if (byId.has(markerId))
|
|
285
290
|
continue;
|
|
286
|
-
const last = lastByEventId.get(eventId) ?? start;
|
|
287
291
|
const marker = {
|
|
288
292
|
id: markerId,
|
|
289
293
|
parentId: turnId,
|
|
@@ -306,6 +310,33 @@ export function markIncompletePersistedTurns(nodes, byId, piboSessionId, events,
|
|
|
306
310
|
}
|
|
307
311
|
return [...incompleteEventIds].some((eventId) => byId.has(`event:incomplete-turn:${eventId}`));
|
|
308
312
|
}
|
|
313
|
+
function settleTurnFromSessionError(byId, eventId, event, completedAt) {
|
|
314
|
+
const turnId = messageTurnNodeId(eventId);
|
|
315
|
+
const turn = byId.get(turnId) ?? [...byId.values()].find((candidate) => candidate.type === "agent.turn" && candidate.eventId === eventId);
|
|
316
|
+
if (!turn)
|
|
317
|
+
return undefined;
|
|
318
|
+
turn.status = "error";
|
|
319
|
+
turn.completedAt = completedAt ?? turn.completedAt;
|
|
320
|
+
turn.summary = event.errorDetails?.userMessage ?? event.error;
|
|
321
|
+
turn.error = event.error;
|
|
322
|
+
settleInterruptedTurnChildren(byId, turn.id, event.error, completedAt);
|
|
323
|
+
return turn.id;
|
|
324
|
+
}
|
|
325
|
+
function settleInterruptedTurnChildren(byId, turnId, error, completedAt) {
|
|
326
|
+
for (const node of byId.values()) {
|
|
327
|
+
if (node.parentId !== turnId)
|
|
328
|
+
continue;
|
|
329
|
+
const isOpenContent = node.status === "running" && (node.type === "assistant.message" ||
|
|
330
|
+
node.type === "model.reasoning" ||
|
|
331
|
+
node.type === "tool.call");
|
|
332
|
+
const isUnfinishedTool = node.type === "tool.call" && node.completedAt === undefined;
|
|
333
|
+
if (!isOpenContent && !isUnfinishedTool)
|
|
334
|
+
continue;
|
|
335
|
+
node.status = "error";
|
|
336
|
+
node.completedAt = completedAt;
|
|
337
|
+
node.error = error;
|
|
338
|
+
}
|
|
339
|
+
}
|
|
309
340
|
export function eventsCanAffectAsyncAgentRunStatus(events) {
|
|
310
341
|
return events.some((event) => {
|
|
311
342
|
const type = event.payload.type;
|
package/dist/subagents/tool.js
CHANGED
|
@@ -34,12 +34,12 @@ function resultText(prefix, value) {
|
|
|
34
34
|
export function normalizePiboAgentSessionName(value) {
|
|
35
35
|
if (typeof value !== "string")
|
|
36
36
|
throw new Error("Agent session name is required.");
|
|
37
|
-
if ([...value].length > PIBO_AGENT_SESSION_NAME_MAX_LENGTH) {
|
|
38
|
-
throw new Error(`Agent session name must be at most ${PIBO_AGENT_SESSION_NAME_MAX_LENGTH} characters.`);
|
|
39
|
-
}
|
|
40
37
|
const normalized = value.trim();
|
|
41
38
|
if (!normalized)
|
|
42
39
|
throw new Error("Agent session name must not be empty.");
|
|
40
|
+
if ([...normalized].length > PIBO_AGENT_SESSION_NAME_MAX_LENGTH) {
|
|
41
|
+
throw new Error(`Agent session name must be at most ${PIBO_AGENT_SESSION_NAME_MAX_LENGTH} characters.`);
|
|
42
|
+
}
|
|
43
43
|
return normalized;
|
|
44
44
|
}
|
|
45
45
|
function preparePiboAgentSessionNameInput(input) {
|
|
@@ -289,10 +289,10 @@ function killLeaseProcess(status, lease) {
|
|
|
289
289
|
}
|
|
290
290
|
async function releaseAssociatedBrowserPoolLease(status, lease) {
|
|
291
291
|
if (!lease.browserPoolLeaseId)
|
|
292
|
-
return;
|
|
292
|
+
return undefined;
|
|
293
293
|
const identity = browserPoolIdentityForLease(status, lease);
|
|
294
294
|
const paths = browserPoolPaths(browserPoolRootForLease(status, lease), identity);
|
|
295
|
-
|
|
295
|
+
return releaseBrowserPoolLease(paths, identity, { leaseId: lease.browserPoolLeaseId });
|
|
296
296
|
}
|
|
297
297
|
async function reapAssociatedBrowserPoolLeaseProcess(status, lease) {
|
|
298
298
|
if (!lease.browserPoolLeaseId)
|
|
@@ -613,7 +613,15 @@ export async function releaseBrowserUseLease(context, id, options = {}) {
|
|
|
613
613
|
}));
|
|
614
614
|
}
|
|
615
615
|
if (lease.browserPoolLeaseId) {
|
|
616
|
-
await releaseAssociatedBrowserPoolLease(context.status, lease);
|
|
616
|
+
const poolRelease = await releaseAssociatedBrowserPoolLease(context.status, lease);
|
|
617
|
+
if (poolRelease && (poolRelease.cleanupStatus === 'failed' || poolRelease.state.state === 'dirty')) {
|
|
618
|
+
throw new Error(formatCliError({
|
|
619
|
+
code: ErrorCode.CLIENT_ERROR,
|
|
620
|
+
type: 'BROWSER_USE_POOL_CLEANUP_FAILED',
|
|
621
|
+
message: `Browser pool cleanup failed for lease "${lease.id}": ${poolRelease.lastError ?? 'managed browser cleanup failed'}`,
|
|
622
|
+
suggestion: `The auth lease and profile were preserved. Run \`pibo tools browser-use pool reap --worker-id ${poolRelease.state.workerId} --pool-id ${poolRelease.state.poolId} --idle-timeout-ms 0\`, then retry release.`,
|
|
623
|
+
}));
|
|
624
|
+
}
|
|
617
625
|
}
|
|
618
626
|
else {
|
|
619
627
|
killLeaseProcess(context.status, lease);
|
package/dist/tools/index.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
export const NODE_RUNTIME_WORKER_SOURCE = String.raw `
|
|
2
2
|
const vm = require("node:vm");
|
|
3
|
+
const inspector = require("node:inspector");
|
|
3
4
|
const util = require("node:util");
|
|
4
5
|
const fs = require("node:fs");
|
|
5
6
|
const childProcess = require("node:child_process");
|
|
@@ -153,6 +154,7 @@ const consoleProxy = {
|
|
|
153
154
|
dir: (value, options) => appendStdout(util.inspect(value, options) + "\n"),
|
|
154
155
|
};
|
|
155
156
|
|
|
157
|
+
const runtimeContextName = "pibo-runtime-" + process.pid;
|
|
156
158
|
const context = vm.createContext({
|
|
157
159
|
console: consoleProxy,
|
|
158
160
|
require: runtimeRequire,
|
|
@@ -169,10 +171,24 @@ const context = vm.createContext({
|
|
|
169
171
|
setImmediate,
|
|
170
172
|
clearImmediate,
|
|
171
173
|
queueMicrotask,
|
|
172
|
-
});
|
|
174
|
+
}, { name: runtimeContextName });
|
|
173
175
|
context.global = context;
|
|
174
176
|
context.globalThis = context;
|
|
175
177
|
|
|
178
|
+
const inspectorSession = new inspector.Session();
|
|
179
|
+
let runtimeContextId;
|
|
180
|
+
inspectorSession.on("Runtime.executionContextCreated", ({ params }) => {
|
|
181
|
+
if (params.context.name === runtimeContextName) runtimeContextId = params.context.id;
|
|
182
|
+
});
|
|
183
|
+
inspectorSession.connect();
|
|
184
|
+
const inspectorReady = inspectorPost("Runtime.enable");
|
|
185
|
+
|
|
186
|
+
function inspectorPost(method, params = {}) {
|
|
187
|
+
return new Promise((resolve, reject) => {
|
|
188
|
+
inspectorSession.post(method, params, (error, result) => error ? reject(error) : resolve(result));
|
|
189
|
+
});
|
|
190
|
+
}
|
|
191
|
+
|
|
176
192
|
async function execute(req) {
|
|
177
193
|
const mode = req.mode || "exec";
|
|
178
194
|
const code = req.code || "";
|
|
@@ -230,16 +246,31 @@ async function inspectValue(req) {
|
|
|
230
246
|
}
|
|
231
247
|
}
|
|
232
248
|
|
|
233
|
-
function listVars(req) {
|
|
249
|
+
async function listVars(req) {
|
|
234
250
|
const includePrivate = Boolean(req.includePrivate);
|
|
235
251
|
const maxItems = Number(req.maxItems || 100);
|
|
236
252
|
const maxBytes = Number(req.maxBytes || 4096);
|
|
237
253
|
const hidden = new Set(["console", "require", "process", "Buffer", "URL", "URLSearchParams", "TextEncoder", "TextDecoder", "global", "globalThis"]);
|
|
254
|
+
const lexicalNames = new Set();
|
|
255
|
+
try {
|
|
256
|
+
await inspectorReady;
|
|
257
|
+
if (runtimeContextId !== undefined) {
|
|
258
|
+
const result = await inspectorPost("Runtime.globalLexicalScopeNames", { executionContextId: runtimeContextId });
|
|
259
|
+
for (const name of result.names || []) lexicalNames.add(name);
|
|
260
|
+
}
|
|
261
|
+
} catch {}
|
|
262
|
+
const names = new Set([...Object.keys(context), ...lexicalNames]);
|
|
238
263
|
const variables = [];
|
|
239
|
-
for (const name of
|
|
264
|
+
for (const name of [...names].sort()) {
|
|
240
265
|
if (hidden.has(name)) continue;
|
|
241
266
|
if (!includePrivate && name.startsWith("_")) continue;
|
|
242
|
-
|
|
267
|
+
let value;
|
|
268
|
+
try {
|
|
269
|
+
value = lexicalNames.has(name) ? vm.runInContext(name, context, { filename: "<pibo-runtime>", timeout: 15000 }) : context[name];
|
|
270
|
+
} catch {
|
|
271
|
+
continue;
|
|
272
|
+
}
|
|
273
|
+
variables.push({ name, summary: summarize(value, maxBytes) });
|
|
243
274
|
if (variables.length >= maxItems) break;
|
|
244
275
|
}
|
|
245
276
|
return { id: req.id, status: "ok", variables, truncated: variables.length >= maxItems };
|
|
@@ -253,7 +284,10 @@ async function handle(req) {
|
|
|
253
284
|
if (req.type === "exec") return await execute(req);
|
|
254
285
|
if (req.type === "inspect") return await inspectValue(req);
|
|
255
286
|
if (req.type === "vars") return listVars(req);
|
|
256
|
-
if (req.type === "shutdown")
|
|
287
|
+
if (req.type === "shutdown") {
|
|
288
|
+
inspectorSession.disconnect();
|
|
289
|
+
return { id: req.id, status: "ok", shutdown: true };
|
|
290
|
+
}
|
|
257
291
|
return { id: req.id, status: "error", error: { name: "RuntimeProtocolError", message: "unknown request type " + req.type } };
|
|
258
292
|
}
|
|
259
293
|
|
|
@@ -90,6 +90,9 @@ export class PythonRuntimeBackend {
|
|
|
90
90
|
isAlive() {
|
|
91
91
|
return this.alive;
|
|
92
92
|
}
|
|
93
|
+
isBusy() {
|
|
94
|
+
return [...this.pending.values()].some((pending) => pending.requestType === "exec");
|
|
95
|
+
}
|
|
93
96
|
getRecord() {
|
|
94
97
|
return { pid: this.child.pid, cwd: this.cwd, executable: this.executable };
|
|
95
98
|
}
|
|
@@ -194,10 +197,11 @@ export class PythonRuntimeBackend {
|
|
|
194
197
|
const id = `req_${++this.requestCounter}`;
|
|
195
198
|
return new Promise((resolveRequest, rejectRequest) => {
|
|
196
199
|
const timer = setTimeout(() => {
|
|
197
|
-
this.pending.
|
|
200
|
+
if (!this.pending.has(id))
|
|
201
|
+
return;
|
|
198
202
|
rejectRequest(new TimeoutError(`Runtime request ${type} timed out after ${timeoutMs}ms`));
|
|
199
203
|
}, timeoutMs);
|
|
200
|
-
this.pending.set(id, { resolve: resolveRequest, reject: rejectRequest, timer });
|
|
204
|
+
this.pending.set(id, { requestType: type, resolve: resolveRequest, reject: rejectRequest, timer });
|
|
201
205
|
this.child.stdin.write(`${JSON.stringify({ id, type, ...payload })}\n`);
|
|
202
206
|
});
|
|
203
207
|
}
|
|
@@ -30,6 +30,7 @@ function notFoundVars(sessionId = "auto") {
|
|
|
30
30
|
export class RuntimeSessionRegistry {
|
|
31
31
|
options;
|
|
32
32
|
sessions = new Map();
|
|
33
|
+
defaultStarts = new Map();
|
|
33
34
|
maxHistoryEntries;
|
|
34
35
|
constructor(options) {
|
|
35
36
|
this.options = options;
|
|
@@ -79,9 +80,23 @@ export class RuntimeSessionRegistry {
|
|
|
79
80
|
}
|
|
80
81
|
}
|
|
81
82
|
async exec(controllerPiboSessionId, input) {
|
|
82
|
-
|
|
83
|
+
let session = input.sessionId
|
|
83
84
|
? this.getSessionForController(controllerPiboSessionId, input.sessionId)
|
|
84
|
-
:
|
|
85
|
+
: this.getDefault(controllerPiboSessionId, input.runtime ?? "python");
|
|
86
|
+
if (!input.sessionId && !session) {
|
|
87
|
+
const runtime = input.runtime ?? "python";
|
|
88
|
+
const started = await this.getOrStartDefault(controllerPiboSessionId, input);
|
|
89
|
+
if (started.status !== "ok" || !started.sessionId) {
|
|
90
|
+
return {
|
|
91
|
+
status: started.status === "ok" ? "failed" : started.status,
|
|
92
|
+
sessionId: started.sessionId ?? "auto",
|
|
93
|
+
runtime: started.runtime ?? runtime,
|
|
94
|
+
durationMs: 0,
|
|
95
|
+
error: started.error ?? { name: "RuntimeStartError", message: "Runtime session failed to start." },
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
session = this.getSessionForController(controllerPiboSessionId, started.sessionId);
|
|
99
|
+
}
|
|
85
100
|
const sessionId = input.sessionId ?? session?.sessionId ?? "auto";
|
|
86
101
|
if (!session)
|
|
87
102
|
return notFoundExec(sessionId);
|
|
@@ -106,7 +121,9 @@ export class RuntimeSessionRegistry {
|
|
|
106
121
|
result.executionCount = session.executionCount;
|
|
107
122
|
session.lastExecAt = startedAt;
|
|
108
123
|
session.updatedAt = nowIso();
|
|
109
|
-
session.status = session.backend.isAlive()
|
|
124
|
+
session.status = session.backend.isAlive()
|
|
125
|
+
? session.backend.isBusy?.() === true ? "busy" : "idle"
|
|
126
|
+
: "failed";
|
|
110
127
|
this.appendHistory(session, {
|
|
111
128
|
id: randomUUID(),
|
|
112
129
|
startedAt,
|
|
@@ -223,14 +240,25 @@ export class RuntimeSessionRegistry {
|
|
|
223
240
|
const runtime = input.runtime ?? "python";
|
|
224
241
|
const existing = this.getDefault(controllerPiboSessionId, runtime);
|
|
225
242
|
if (existing)
|
|
226
|
-
return existing;
|
|
227
|
-
const
|
|
243
|
+
return { status: "ok", sessionId: existing.sessionId, runtime: existing.runtime };
|
|
244
|
+
const key = `${controllerPiboSessionId}\0${runtime}`;
|
|
245
|
+
const pending = this.defaultStarts.get(key);
|
|
246
|
+
if (pending)
|
|
247
|
+
return pending;
|
|
248
|
+
const start = this.start(controllerPiboSessionId, {
|
|
228
249
|
runtime,
|
|
229
250
|
name: input.name,
|
|
230
251
|
target: input.target,
|
|
231
252
|
timeoutMs: input.timeoutMs,
|
|
232
253
|
});
|
|
233
|
-
|
|
254
|
+
this.defaultStarts.set(key, start);
|
|
255
|
+
try {
|
|
256
|
+
return await start;
|
|
257
|
+
}
|
|
258
|
+
finally {
|
|
259
|
+
if (this.defaultStarts.get(key) === start)
|
|
260
|
+
this.defaultStarts.delete(key);
|
|
261
|
+
}
|
|
234
262
|
}
|
|
235
263
|
getSessionForController(controllerPiboSessionId, sessionId) {
|
|
236
264
|
const session = this.sessions.get(sessionId);
|
|
@@ -243,6 +271,10 @@ export class RuntimeSessionRegistry {
|
|
|
243
271
|
session.status = "failed";
|
|
244
272
|
session.updatedAt = nowIso();
|
|
245
273
|
}
|
|
274
|
+
else if (session.status === "busy" && session.backend.isBusy?.() === false) {
|
|
275
|
+
session.status = "idle";
|
|
276
|
+
session.updatedAt = nowIso();
|
|
277
|
+
}
|
|
246
278
|
return session;
|
|
247
279
|
}
|
|
248
280
|
appendHistory(session, entry) {
|
package/dist/vscode/cli.js
CHANGED
|
@@ -50,9 +50,8 @@ export async function runVscodeCli(argv = process.argv) {
|
|
|
50
50
|
else if (result.status === "installed") {
|
|
51
51
|
console.log(`installed\t${result.tagName}\t${result.vsixPath}`);
|
|
52
52
|
}
|
|
53
|
-
|
|
53
|
+
if (result.status === "failed")
|
|
54
54
|
process.exitCode = 1;
|
|
55
|
-
}
|
|
56
55
|
});
|
|
57
56
|
program
|
|
58
57
|
.command("uninstall")
|