@pasko70/pibo 3.2.3 → 3.2.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/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-CTsyojIh.js → dist-CTC-_3l0.js} +1 -1
- package/dist/apps/chat-ui/assets/{dist-BHio-Mzx.js → dist-CftLEFd5.js} +1 -1
- package/dist/apps/chat-ui/assets/{dist-DJ6Bhcuk.js → dist-Dq27Wt2q.js} +1 -1
- package/dist/apps/chat-ui/assets/{dist-CPKEoSPK.js → dist-Y4fHV5-Z.js} +1 -1
- package/dist/apps/chat-ui/assets/{dist-DNHFRMCk.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-Dc-6Xupt.css → index-DUGVw259.css} +1 -1
- package/dist/apps/chat-ui/index.html +2 -2
- package/dist/apps/chat-vscode-web/assets/{index-B1TuQNY5.js → index-B5X0NaRo.js} +4 -4
- 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 +55 -7
- package/dist/data/schema.js +65 -23
- 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/mcp-bridge.js +1 -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-Y6jYzsaC.js +0 -228
package/dist/loops/service.js
CHANGED
|
@@ -20,6 +20,9 @@ class LoopRunTimeoutError extends Error {
|
|
|
20
20
|
this.name = 'LoopRunTimeoutError';
|
|
21
21
|
}
|
|
22
22
|
}
|
|
23
|
+
export class PiboLoopCapacityError extends Error {
|
|
24
|
+
constructor() { super('Loop run capacity is currently full'); this.name = 'PiboLoopCapacityError'; }
|
|
25
|
+
}
|
|
23
26
|
class LoopContinuationInvalidatedError extends Error {
|
|
24
27
|
constructor(message) { super(message); this.name = 'LoopContinuationInvalidatedError'; }
|
|
25
28
|
}
|
|
@@ -84,6 +87,7 @@ export class PiboLoopService {
|
|
|
84
87
|
random;
|
|
85
88
|
timer;
|
|
86
89
|
activeRuns = 0;
|
|
90
|
+
admissionTail = Promise.resolve();
|
|
87
91
|
activeRunWork = new Set();
|
|
88
92
|
pendingRunStarts = 0;
|
|
89
93
|
schedulerWork;
|
|
@@ -143,14 +147,21 @@ export class PiboLoopService {
|
|
|
143
147
|
return undefined;
|
|
144
148
|
this.pendingRunStarts += 1;
|
|
145
149
|
try {
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
150
|
+
return await this.withAdmissionLock(async () => {
|
|
151
|
+
const existing = this.store.getJob(id);
|
|
152
|
+
if (!existing || existing.state.runningAt)
|
|
153
|
+
return undefined;
|
|
154
|
+
if (this.activeRuns >= this.maxConcurrentRuns)
|
|
155
|
+
throw new PiboLoopCapacityError();
|
|
156
|
+
const job = this.store.updateJob(id, { enabled: true });
|
|
157
|
+
if (!job)
|
|
158
|
+
return undefined;
|
|
159
|
+
const reserved = await this.reserveAfterBeforeRunEvaluation(job);
|
|
160
|
+
if (!reserved)
|
|
161
|
+
return undefined;
|
|
162
|
+
this.launchReservedRun(reserved.job, reserved.run);
|
|
163
|
+
return reserved.run;
|
|
164
|
+
});
|
|
154
165
|
}
|
|
155
166
|
finally {
|
|
156
167
|
this.pendingRunStarts -= 1;
|
|
@@ -303,14 +314,13 @@ export class PiboLoopService {
|
|
|
303
314
|
async tick() { if (this.stopped)
|
|
304
315
|
return; try {
|
|
305
316
|
await this.abortCancelRequestedJobs();
|
|
306
|
-
const
|
|
307
|
-
|
|
308
|
-
const
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
}
|
|
317
|
+
const jobs = this.store.listJobs({ includeDisabled: false }).filter((job) => !job.state.runningAt);
|
|
318
|
+
for (const job of jobs) {
|
|
319
|
+
const admitted = await this.withAdmissionLock(async () => { if (this.activeRuns >= this.maxConcurrentRuns)
|
|
320
|
+
return false; const reserved = await this.reserveAfterBeforeRunEvaluation(job); if (!reserved)
|
|
321
|
+
return true; this.launchReservedRun(reserved.job, reserved.run); return true; });
|
|
322
|
+
if (!admitted)
|
|
323
|
+
break;
|
|
314
324
|
}
|
|
315
325
|
}
|
|
316
326
|
catch (error) {
|
|
@@ -319,6 +329,12 @@ export class PiboLoopService {
|
|
|
319
329
|
finally {
|
|
320
330
|
this.arm();
|
|
321
331
|
} }
|
|
332
|
+
async withAdmissionLock(action) { const previous = this.admissionTail; let release; this.admissionTail = new Promise((resolve) => { release = resolve; }); await previous; try {
|
|
333
|
+
return await action();
|
|
334
|
+
}
|
|
335
|
+
finally {
|
|
336
|
+
release();
|
|
337
|
+
} }
|
|
322
338
|
async reserveAfterBeforeRunEvaluation(job) {
|
|
323
339
|
const fresh = this.store.getJob(job.id) ?? job;
|
|
324
340
|
const now = this.now();
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { createHash } from "node:crypto";
|
|
2
2
|
import { Client } from "@modelcontextprotocol/sdk/client/index.js";
|
|
3
|
+
import { redactSensitiveText } from "../core/sensitive-data-redaction.js";
|
|
3
4
|
import { getDefaultEnvironment, StdioClientTransport } from "@modelcontextprotocol/sdk/client/stdio.js";
|
|
4
5
|
import { StreamableHTTPClientTransport } from "@modelcontextprotocol/sdk/client/streamableHttp.js";
|
|
5
6
|
import { filterTools, isHttpServer, } from "./config.js";
|
|
@@ -94,7 +95,7 @@ export function redactMcpRuntimeError(error, secretValues = []) {
|
|
|
94
95
|
if (value)
|
|
95
96
|
message = message.split(value).join("[REDACTED]");
|
|
96
97
|
}
|
|
97
|
-
return message.
|
|
98
|
+
return redactSensitiveText(message).replaceAll("[redacted]", "[REDACTED]");
|
|
98
99
|
}
|
|
99
100
|
export async function verifyPiboMcpServer(serverName, config, options) {
|
|
100
101
|
const client = new Client({ name: "pibo-runtime-resource-verifier", version: "1.0.0" }, { capabilities: {} });
|
package/dist/pi-packages/cli.js
CHANGED
|
@@ -51,6 +51,7 @@ export async function runPiPackagesCli(argv = process.argv) {
|
|
|
51
51
|
console.log("No Pi packages registered.");
|
|
52
52
|
return;
|
|
53
53
|
}
|
|
54
|
+
let hasErrors = false;
|
|
54
55
|
for (const pkg of packages) {
|
|
55
56
|
const diagnostics = [];
|
|
56
57
|
try {
|
|
@@ -68,11 +69,15 @@ export async function runPiPackagesCli(argv = process.argv) {
|
|
|
68
69
|
}
|
|
69
70
|
}
|
|
70
71
|
const status = diagnostics.length || pkg.installStatus === "error" || pkg.installStatus === "missing" ? "error" : "ok";
|
|
72
|
+
if (status === "error")
|
|
73
|
+
hasErrors = true;
|
|
71
74
|
console.log(`${pkg.name}\t${status}\t${pkg.installSpec}`);
|
|
72
75
|
for (const diagnostic of [...pkg.diagnostics.map((item) => item.message), ...diagnostics]) {
|
|
73
76
|
console.log(` ${diagnostic}`);
|
|
74
77
|
}
|
|
75
78
|
}
|
|
79
|
+
if (hasErrors)
|
|
80
|
+
process.exitCode = 1;
|
|
76
81
|
});
|
|
77
82
|
await program.parseAsync(argv);
|
|
78
83
|
}
|
package/dist/previews/cli.js
CHANGED
|
@@ -1,9 +1,8 @@
|
|
|
1
1
|
import { randomBytes } from "node:crypto";
|
|
2
2
|
import { resolve } from "node:path";
|
|
3
3
|
import { Command } from "commander";
|
|
4
|
-
import { sanitizePreviewServerSettings } from "../core/preview-server-settings.js";
|
|
5
4
|
import { createDefaultPiboDataSessionStore } from "../sessions/pibo-data-store.js";
|
|
6
|
-
import { DEFAULT_PREVIEW_TTL_MINUTES, loadPreviewConfig, previewPublicURL, requirePreviewBaseURL } from "./config.js";
|
|
5
|
+
import { DEFAULT_PREVIEW_TTL_MINUTES, loadEffectivePreviewServerSettings, loadPreviewConfig, previewPublicURL, requirePreviewBaseURL } from "./config.js";
|
|
7
6
|
import { reconcileManagedPreviews, startManagedPreview, stopManagedPreview, validatePreviewStartCommand, } from "./manager.js";
|
|
8
7
|
import { findPreviewTargetProcess, isPiboControlProcess, isPiboYieldedProcess, isPreviewTargetProcessCurrent, probePreviewTarget, validatePreviewPort, } from "./network.js";
|
|
9
8
|
import { createDefaultPreviewStore, previewExposureState } from "./store.js";
|
|
@@ -264,10 +263,7 @@ export async function runPreviewCli(argv = process.argv) {
|
|
|
264
263
|
const baseURL = requirePreviewBaseURL();
|
|
265
264
|
if (!id) {
|
|
266
265
|
const config = loadPreviewConfig();
|
|
267
|
-
const serverLimits =
|
|
268
|
-
maxRunningServers: config.maxRunningServers,
|
|
269
|
-
autoStopMinutes: config.autoStopMinutes,
|
|
270
|
-
});
|
|
266
|
+
const serverLimits = loadEffectivePreviewServerSettings();
|
|
271
267
|
const store = createDefaultPreviewStore();
|
|
272
268
|
let diagnostics;
|
|
273
269
|
try {
|
package/dist/previews/config.js
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import { loadPiboConfig } from "../config/config.js";
|
|
2
|
+
import { sanitizePreviewServerSettings } from "../core/preview-server-settings.js";
|
|
3
|
+
import { loadPiboUserSettings } from "../core/user-settings.js";
|
|
2
4
|
import { parsePreviewBaseURL } from "./base-url.js";
|
|
3
5
|
export const DEFAULT_PREVIEW_TTL_MINUTES = 8 * 60;
|
|
4
6
|
export const DEFAULT_PREVIEW_TICKET_TTL_SECONDS = 60;
|
|
@@ -6,6 +8,14 @@ export const DEFAULT_PREVIEW_SESSION_TTL_MINUTES = 8 * 60;
|
|
|
6
8
|
export function loadPreviewConfig() {
|
|
7
9
|
return loadPiboConfig().preview ?? {};
|
|
8
10
|
}
|
|
11
|
+
export function loadEffectivePreviewServerSettings() {
|
|
12
|
+
const config = loadPreviewConfig();
|
|
13
|
+
const userSettings = loadPiboUserSettings().previewServers;
|
|
14
|
+
return sanitizePreviewServerSettings({
|
|
15
|
+
maxRunningServers: config.maxRunningServers ?? userSettings.maxRunningServers,
|
|
16
|
+
autoStopMinutes: config.autoStopMinutes ?? userSettings.autoStopMinutes,
|
|
17
|
+
});
|
|
18
|
+
}
|
|
9
19
|
export function requirePreviewBaseURL(value = loadPreviewConfig().baseURL) {
|
|
10
20
|
if (!value)
|
|
11
21
|
throw new Error("preview.baseURL is required. Set it with `pibo config set preview.baseURL https://preview.example.com`.");
|
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
package/dist/tools/mcp-bridge.js
CHANGED
|
@@ -180,7 +180,7 @@ async function piboResultToMcp(result, options) {
|
|
|
180
180
|
tool: options.tool,
|
|
181
181
|
toolCallId: options.toolCallId,
|
|
182
182
|
contentType: "application/json",
|
|
183
|
-
value:
|
|
183
|
+
value: structuredContent,
|
|
184
184
|
});
|
|
185
185
|
payloadRefs.push(stored.ref);
|
|
186
186
|
structuredContent = undefined;
|