@pasko70/pibo 2.3.0 → 2.4.1
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/routed-session.js +1 -0
- package/dist/agent-runtimes/codex-native/turn.js +3 -1
- package/dist/agent-runtimes/omp/turn.js +36 -7
- package/dist/apps/chat/web-app.js +6 -8
- package/dist/apps/chat-ui/assets/{dist-CUcAofmV.js → dist-3YG57JXi.js} +1 -1
- package/dist/apps/chat-ui/assets/{dist-Byygd1lH.js → dist-BeqHbnGN.js} +1 -1
- package/dist/apps/chat-ui/assets/{dist-DusFwy0L.js → dist-CrDtveZB.js} +1 -1
- package/dist/apps/chat-ui/assets/{dist-C9BrS7sL.js → dist-Cw9po47P.js} +1 -1
- package/dist/apps/chat-ui/assets/{dist-D4RU6xu3.js → dist-DTRjeLwO.js} +1 -1
- package/dist/apps/chat-ui/assets/{index-Bifi_kjN.js → index-AjnP3ci-.js} +89 -89
- package/dist/apps/chat-ui/index.html +1 -1
- package/dist/apps/chat-vscode-web/assets/{index-WsLm1mo3.js → index-DvTSSvzN.js} +5 -5
- package/dist/apps/chat-vscode-web/index.html +1 -1
- package/dist/apps/vscode-artifacts/latest.vsix +0 -0
- package/dist/apps/vscode-artifacts/{pibo-vscode-ext-2.3.0.vsix → pibo-vscode-ext-2.4.1.vsix} +0 -0
- package/dist/compute/cli.js +13 -0
- package/dist/compute/pool/artifacts.js +116 -0
- package/dist/compute/pool/cli.js +156 -0
- package/dist/compute/pool/config.js +101 -0
- package/dist/compute/pool/docker.js +157 -0
- package/dist/compute/pool/seeds.js +201 -0
- package/dist/compute/pool/service.js +402 -0
- package/dist/compute/pool/store.js +239 -0
- package/dist/compute/pool/types.js +1 -0
- package/dist/gateway/web.js +8 -1
- package/dist/loops/accounting.js +27 -0
- package/dist/loops/cli.js +6 -5
- package/dist/loops/prompts.js +13 -5
- package/dist/loops/service.js +3 -1
- package/dist/loops/store.js +10 -6
- package/dist/loops/tools.js +7 -4
- package/dist/mcp/config-command.js +3 -2
- package/dist/mcp/config.js +10 -4
- package/dist/mcp/errors.js +1 -1
- package/dist/mcp/index.js +19 -33
- package/dist/resources/lifecycle.js +22 -2
- package/dist/resources/reaper.js +1 -0
- package/dist/session-ui/sessionActivity.js +6 -2
- package/dist/signals/status.js +9 -4
- package/dist/tools/guides.js +1 -1
- package/dist/web/channel.js +19 -7
- package/npm-shrinkwrap.json +2 -2
- package/package.json +1 -1
- package/skills/builtin/loop/SKILL.md +1 -1
package/dist/mcp/index.js
CHANGED
|
@@ -9,9 +9,8 @@
|
|
|
9
9
|
* mcp-cli call <server> <tool> Call tool (reads JSON from stdin if no args)
|
|
10
10
|
* mcp-cli call <server> <tool> {} Call tool with JSON args
|
|
11
11
|
*/
|
|
12
|
-
import { readFileSync } from 'node:fs';
|
|
13
12
|
import { configCommand } from './config-command.js';
|
|
14
|
-
import { ensureConfigExists, } from './config.js';
|
|
13
|
+
import { ensureConfigExists, listServerNames, loadConfigUnresolved, } from './config.js';
|
|
15
14
|
import { ErrorCode, ambiguousCommandError, formatCliError, missingArgumentError, tooManyArgumentsError, unknownOptionError, unknownSubcommandError, } from './errors.js';
|
|
16
15
|
import { registryCommand } from './registry.js';
|
|
17
16
|
import { VERSION } from './version.js';
|
|
@@ -127,37 +126,8 @@ function parseArgs(args) {
|
|
|
127
126
|
result.command = 'info';
|
|
128
127
|
const remaining = positional.slice(1);
|
|
129
128
|
const { server, tool } = parseServerTool(remaining);
|
|
130
|
-
// info requires a server argument - show available servers in error
|
|
131
129
|
if (!server) {
|
|
132
|
-
|
|
133
|
-
let availableServers = [];
|
|
134
|
-
const configPaths = [
|
|
135
|
-
result.configPath,
|
|
136
|
-
process.env.MCP_CONFIG_PATH,
|
|
137
|
-
'./mcp_servers.json',
|
|
138
|
-
`${process.env.HOME}/.mcp_servers.json`,
|
|
139
|
-
`${process.env.HOME}/.config/mcp/mcp_servers.json`,
|
|
140
|
-
].filter(Boolean);
|
|
141
|
-
for (const cfgPath of configPaths) {
|
|
142
|
-
try {
|
|
143
|
-
const content = readFileSync(cfgPath, 'utf-8');
|
|
144
|
-
const config = JSON.parse(content);
|
|
145
|
-
if (config.mcpServers) {
|
|
146
|
-
availableServers = Object.keys(config.mcpServers);
|
|
147
|
-
break;
|
|
148
|
-
}
|
|
149
|
-
}
|
|
150
|
-
catch {
|
|
151
|
-
// Try next path
|
|
152
|
-
}
|
|
153
|
-
}
|
|
154
|
-
const serverList = availableServers.length > 0
|
|
155
|
-
? availableServers.join(', ')
|
|
156
|
-
: '(none found)';
|
|
157
|
-
console.error('Error [MISSING_ARGUMENT]: Missing required argument for info: server');
|
|
158
|
-
console.error(` Available servers: ${serverList}`);
|
|
159
|
-
console.error(` Suggestion: Use 'pibo mcp info <server>' to see server details, or just 'pibo mcp' to list all`);
|
|
160
|
-
process.exit(ErrorCode.CLIENT_ERROR);
|
|
130
|
+
return result;
|
|
161
131
|
}
|
|
162
132
|
result.server = server;
|
|
163
133
|
result.tool = tool;
|
|
@@ -371,10 +341,26 @@ export async function runMcpCli(argv = process.argv) {
|
|
|
371
341
|
}
|
|
372
342
|
break;
|
|
373
343
|
case 'info':
|
|
344
|
+
if (!args.server) {
|
|
345
|
+
let availableServers = [];
|
|
346
|
+
try {
|
|
347
|
+
availableServers = listServerNames(await loadConfigUnresolved(args.configPath));
|
|
348
|
+
}
|
|
349
|
+
catch {
|
|
350
|
+
// Keep the missing-argument error focused when no valid config exists.
|
|
351
|
+
}
|
|
352
|
+
const serverList = availableServers.length > 0
|
|
353
|
+
? availableServers.join(', ')
|
|
354
|
+
: '(none found)';
|
|
355
|
+
console.error('Error [MISSING_ARGUMENT]: Missing required argument for info: server');
|
|
356
|
+
console.error(` Available servers: ${serverList}`);
|
|
357
|
+
console.error(` Suggestion: Use 'pibo mcp info <server>' to see server details, or just 'pibo mcp' to list all`);
|
|
358
|
+
process.exitCode = ErrorCode.CLIENT_ERROR;
|
|
359
|
+
break;
|
|
360
|
+
}
|
|
374
361
|
await ensureConfigExists(args.configPath);
|
|
375
362
|
{
|
|
376
363
|
const { infoCommand } = await import('./commands/info.js');
|
|
377
|
-
// info always has a server (validated in parseArgs)
|
|
378
364
|
await infoCommand({
|
|
379
365
|
target: buildTarget(args.server, args.tool),
|
|
380
366
|
withDescriptions: args.withDescriptions,
|
|
@@ -6,6 +6,8 @@ import { promisify } from "node:util";
|
|
|
6
6
|
import { applyComputeWorkerReapPlan, buildComputeWorkerReapPlan, planReapWorkers, } from "../compute/docker.js";
|
|
7
7
|
import { defaultBrowserPoolRoot, defaultBrowserUseHome, getComputeResourceHealth, parseProcessList, } from "../compute/resource-health.js";
|
|
8
8
|
import { loadBrowserPoolState, reapIdleBrowserPool, } from "../tools/browser-pool.js";
|
|
9
|
+
import { resolveDeploymentPoolConfig } from "../compute/pool/config.js";
|
|
10
|
+
import { applyDeploymentPoolReapPlan, planDeploymentPoolReap } from "../compute/pool/service.js";
|
|
9
11
|
const execFileAsync = promisify(execFile);
|
|
10
12
|
export async function collectManagedBrowserPools(rootDir) {
|
|
11
13
|
const records = [];
|
|
@@ -56,7 +58,7 @@ export async function getActiveResourceLeases(browserPoolRoot = defaultBrowserPo
|
|
|
56
58
|
export async function planResourceReap(options = {}) {
|
|
57
59
|
const now = options.now ?? new Date();
|
|
58
60
|
const resolved = resolveReapOptions(options);
|
|
59
|
-
const [records, staleFiles, compute, health] = await Promise.all([
|
|
61
|
+
const [records, staleFiles, compute, health, deploymentPool] = await Promise.all([
|
|
60
62
|
collectManagedBrowserPools(resolved.browserPoolRoot),
|
|
61
63
|
planStaleCdpFiles(resolved.browserUseHome),
|
|
62
64
|
planComputeReapSafely({ includeDev: resolved.includeDev, maxAgeMinutes: resolved.maxAgeMinutes, now }),
|
|
@@ -66,9 +68,10 @@ export async function planResourceReap(options = {}) {
|
|
|
66
68
|
browserUseHome: resolved.browserUseHome,
|
|
67
69
|
exemptBrowserUserDataDirs: [],
|
|
68
70
|
}),
|
|
71
|
+
planDeploymentPoolReapSafely(now),
|
|
69
72
|
]);
|
|
70
73
|
const unmanagedBrowsers = buildUnmanagedBrowserPlanItems(health.browserProcesses.unassignedMainProcessDetails, resolved.unmanagedBrowserGraceMinutes, new Set(resolved.exemptBrowserPids), new Set(resolved.exemptBrowserUserDataDirs), resolved.browserUseHome);
|
|
71
|
-
return buildResourceReapPlan({ now, options: resolved, records, staleFiles, unmanagedBrowsers, compute });
|
|
74
|
+
return buildResourceReapPlan({ now, options: resolved, records, staleFiles, unmanagedBrowsers, compute, deploymentPool });
|
|
72
75
|
}
|
|
73
76
|
export function buildResourceReapPlan(input) {
|
|
74
77
|
const browserItems = input.records.map((record) => buildBrowserReapPlanItem(record, input.now, input.options.idleTimeoutMinutes));
|
|
@@ -89,6 +92,7 @@ export function buildResourceReapPlan(input) {
|
|
|
89
92
|
skipped: unmanagedBrowsers.filter((item) => item.action === "skip").length,
|
|
90
93
|
},
|
|
91
94
|
compute: input.compute,
|
|
95
|
+
deploymentPool: input.deploymentPool,
|
|
92
96
|
worktreesPreserved: true,
|
|
93
97
|
};
|
|
94
98
|
}
|
|
@@ -112,6 +116,9 @@ export async function applyResourceReapPlan(plan, dependencies = {}) {
|
|
|
112
116
|
}
|
|
113
117
|
const removedStaleFiles = await applyStaleCdpFilePlan(confirmed.staleFiles.items, dependencies.isPidAlive);
|
|
114
118
|
const removedComputeWorkers = await (dependencies.applyCompute ?? applyComputeWorkerReapPlan)(confirmed.compute);
|
|
119
|
+
const deploymentPoolResult = confirmed.deploymentPool
|
|
120
|
+
? await (dependencies.applyDeploymentPool ?? applyDeploymentPoolReapPlan)(confirmed.deploymentPool)
|
|
121
|
+
: undefined;
|
|
115
122
|
return {
|
|
116
123
|
applied: true,
|
|
117
124
|
plan: confirmed,
|
|
@@ -119,6 +126,8 @@ export async function applyResourceReapPlan(plan, dependencies = {}) {
|
|
|
119
126
|
terminatedUnmanagedBrowsers,
|
|
120
127
|
removedStaleFiles,
|
|
121
128
|
removedComputeWorkers,
|
|
129
|
+
deploymentPoolResult,
|
|
130
|
+
removedDeploymentLeases: deploymentPoolResult?.releasedLeases ?? [],
|
|
122
131
|
worktreesPreserved: true,
|
|
123
132
|
};
|
|
124
133
|
}
|
|
@@ -287,6 +296,17 @@ export async function planComputeReapSafely(options, planCompute = planReapWorke
|
|
|
287
296
|
return plan;
|
|
288
297
|
}
|
|
289
298
|
}
|
|
299
|
+
async function planDeploymentPoolReapSafely(now) {
|
|
300
|
+
const config = resolveDeploymentPoolConfig();
|
|
301
|
+
if (!existsSync(config.databasePath))
|
|
302
|
+
return undefined;
|
|
303
|
+
try {
|
|
304
|
+
return await planDeploymentPoolReap({ config, now });
|
|
305
|
+
}
|
|
306
|
+
catch {
|
|
307
|
+
return undefined;
|
|
308
|
+
}
|
|
309
|
+
}
|
|
290
310
|
async function planStaleCdpFiles(browserUseHome, isPidAlive = defaultIsPidAlive) {
|
|
291
311
|
const stateDir = join(browserUseHome, "pibo-cdp");
|
|
292
312
|
let files;
|
package/dist/resources/reaper.js
CHANGED
|
@@ -95,6 +95,7 @@ export class ResourceReaperService {
|
|
|
95
95
|
unmanagedBrowsers: result.terminatedUnmanagedBrowsers.length,
|
|
96
96
|
staleFiles: result.removedStaleFiles.length,
|
|
97
97
|
computeWorkers: result.removedComputeWorkers.length,
|
|
98
|
+
...(result.deploymentPoolResult ? { deploymentLeases: result.removedDeploymentLeases?.length ?? 0 } : {}),
|
|
98
99
|
},
|
|
99
100
|
lastError: undefined,
|
|
100
101
|
};
|
|
@@ -1,12 +1,16 @@
|
|
|
1
|
+
import { resolveSessionSignalStatus } from "../signals/status.js";
|
|
1
2
|
export function resolveSessionActivity(signal, fallback = {}) {
|
|
2
3
|
if (!signal)
|
|
3
4
|
return fallbackSessionActivity(fallback);
|
|
4
5
|
const latestTurn = signal.latestTurn;
|
|
5
6
|
const isTurnActive = latestTurn?.state === "running";
|
|
6
7
|
const isTreeActive = signal.isTreeActive || isTurnActive;
|
|
7
|
-
const hasError = signal.hasError || signal.hasErrorDescendant || signal.aggregateStatus === "error";
|
|
8
8
|
return {
|
|
9
|
-
status:
|
|
9
|
+
status: resolveSessionSignalStatus({
|
|
10
|
+
isTreeActive,
|
|
11
|
+
localStatus: signal.localStatus,
|
|
12
|
+
latestTurn,
|
|
13
|
+
}),
|
|
10
14
|
isTreeActive,
|
|
11
15
|
isTurnActive,
|
|
12
16
|
activeTurnId: isTurnActive ? latestTurn.eventId : undefined,
|
package/dist/signals/status.js
CHANGED
|
@@ -1,12 +1,17 @@
|
|
|
1
|
+
export function resolveSessionSignalStatus(snapshot) {
|
|
2
|
+
if (snapshot.isTreeActive || snapshot.latestTurn?.state === "running")
|
|
3
|
+
return "running";
|
|
4
|
+
if (snapshot.localStatus === "error" || snapshot.latestTurn?.state === "failed")
|
|
5
|
+
return "error";
|
|
6
|
+
return "idle";
|
|
7
|
+
}
|
|
1
8
|
export function summarizeSessionSignalStatus(snapshot) {
|
|
2
|
-
const
|
|
3
|
-
const hasError = snapshot.hasError || snapshot.hasErrorDescendant || snapshot.aggregateStatus === "error";
|
|
4
|
-
const isTreeActive = snapshot.isTreeActive || isTurnActive;
|
|
9
|
+
const isTreeActive = snapshot.isTreeActive || snapshot.latestTurn?.state === "running";
|
|
5
10
|
return {
|
|
6
11
|
piboSessionId: snapshot.piboSessionId,
|
|
7
12
|
rootPiboSessionId: snapshot.rootPiboSessionId,
|
|
8
13
|
updatedAt: snapshot.updatedAt,
|
|
9
|
-
status:
|
|
14
|
+
status: resolveSessionSignalStatus(snapshot),
|
|
10
15
|
isTreeActive,
|
|
11
16
|
};
|
|
12
17
|
}
|
package/dist/tools/guides.js
CHANGED
|
@@ -46,7 +46,7 @@ pibo loop stop <job-id>
|
|
|
46
46
|
pibo loop cancel <job-id>
|
|
47
47
|
\`\`\`
|
|
48
48
|
|
|
49
|
-
|
|
49
|
+
New Goals persist token-accounting version \`1\` with basis \`uncached\`: their budgets count uncached input and output usage reported by completed assistant model messages, while cache-read and cache-write tokens remain telemetry and do not consume the budget. Legacy persisted Goal jobs and runs without an accounting descriptor remain on version \`1\` basis \`total\`, including cache reads and writes, so their existing counters are neither relabeled nor numerically reconstructed without source data. Inspect \`pibo loop list --all --json\` and \`pibo loop runs --job <job-id> --json\` for the persisted basis. Both bases are soft because usage arrives after the response, so one request can overshoot.
|
|
50
50
|
`,
|
|
51
51
|
};
|
|
52
52
|
export const RALPH_GUIDE = {
|
package/dist/web/channel.js
CHANGED
|
@@ -78,7 +78,13 @@ export function stripSocketPeerHeaderFromResponse(response) {
|
|
|
78
78
|
headers,
|
|
79
79
|
});
|
|
80
80
|
}
|
|
81
|
-
function createRequestBaseURL(nodeRequest, host, port) {
|
|
81
|
+
function createRequestBaseURL(nodeRequest, host, port, canonicalBaseURL) {
|
|
82
|
+
const requestHost = firstHeaderValue(nodeRequest.headers.host);
|
|
83
|
+
if (canonicalBaseURL && requestHost) {
|
|
84
|
+
const canonical = new URL(canonicalBaseURL);
|
|
85
|
+
if (requestHost.toLowerCase() === canonical.host.toLowerCase())
|
|
86
|
+
return canonical.origin;
|
|
87
|
+
}
|
|
82
88
|
if (isLoopbackAddress(nodeRequest.socket.remoteAddress)) {
|
|
83
89
|
const forwardedHost = firstHeaderValue(nodeRequest.headers["x-forwarded-host"]);
|
|
84
90
|
const forwardedProto = firstHeaderValue(nodeRequest.headers["x-forwarded-proto"]);
|
|
@@ -86,7 +92,7 @@ function createRequestBaseURL(nodeRequest, host, port) {
|
|
|
86
92
|
return `${forwardedProto}://${forwardedHost}`;
|
|
87
93
|
}
|
|
88
94
|
}
|
|
89
|
-
return `http://${
|
|
95
|
+
return `http://${requestHost ?? `${host}:${port}`}`;
|
|
90
96
|
}
|
|
91
97
|
function isActiveRunStatus(status) {
|
|
92
98
|
return typeof status === "string" && ["queued", "starting", "running", "streaming", "waiting", "blocked", "retrying", "compacting", "pausing"].includes(status);
|
|
@@ -235,7 +241,7 @@ export function createWebHostChannel(options = {}) {
|
|
|
235
241
|
};
|
|
236
242
|
const handleRequest = async (nodeRequest, nodeResponse) => {
|
|
237
243
|
try {
|
|
238
|
-
const baseURL = createRequestBaseURL(nodeRequest, host, port);
|
|
244
|
+
const baseURL = createRequestBaseURL(nodeRequest, host, port, options.canonicalBaseURL);
|
|
239
245
|
const baseRequest = await nodeRequestToWebRequest(nodeRequest, baseURL);
|
|
240
246
|
// Inject the TCP socket peer into every request so the local auth
|
|
241
247
|
// plugin can apply the same loopback predicate from `getSession`
|
|
@@ -278,11 +284,17 @@ export function createWebHostChannel(options = {}) {
|
|
|
278
284
|
await sendResponse(nodeResponse, response ?? notFound());
|
|
279
285
|
return;
|
|
280
286
|
}
|
|
281
|
-
if (url.pathname === "/" && apps[0]) {
|
|
282
|
-
await sendResponse(nodeResponse, redirect(apps[0].mountPath));
|
|
283
|
-
return;
|
|
284
|
-
}
|
|
285
287
|
if (url.pathname === "/") {
|
|
288
|
+
const landingApp = options.landingAppName
|
|
289
|
+
? apps.find((candidate) => candidate.name === options.landingAppName)
|
|
290
|
+
: apps[0];
|
|
291
|
+
if (options.landingAppName && !landingApp) {
|
|
292
|
+
throw new Error(`Configured landing web app "${options.landingAppName}" is not registered`);
|
|
293
|
+
}
|
|
294
|
+
if (landingApp) {
|
|
295
|
+
await sendResponse(nodeResponse, redirect(`${landingApp.mountPath}${url.search}`));
|
|
296
|
+
return;
|
|
297
|
+
}
|
|
286
298
|
await sendResponse(nodeResponse, responseHtml("<!doctype html><title>Pibo</title><p>No web apps registered.</p>"));
|
|
287
299
|
return;
|
|
288
300
|
}
|
package/npm-shrinkwrap.json
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pasko70/pibo",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.4.1",
|
|
4
4
|
"lockfileVersion": 3,
|
|
5
5
|
"requires": true,
|
|
6
6
|
"packages": {
|
|
7
7
|
"": {
|
|
8
8
|
"name": "@pasko70/pibo",
|
|
9
|
-
"version": "2.
|
|
9
|
+
"version": "2.4.1",
|
|
10
10
|
"workspaces": [
|
|
11
11
|
"packages/workflows"
|
|
12
12
|
],
|
package/package.json
CHANGED
|
@@ -51,7 +51,7 @@ Prefer creating the job stopped when its prompt, target, profile, or safety boun
|
|
|
51
51
|
|
|
52
52
|
## Token budgets
|
|
53
53
|
|
|
54
|
-
Goal token budgets are soft
|
|
54
|
+
Goal token budgets are soft because usage arrives after model responses, so the final turn can overshoot. New Goals use versioned `uncached` accounting: cache-read and cache-write tokens do not consume the budget. Legacy persisted Goals without an accounting descriptor remain on `total` accounting, including cache usage, because their existing counters cannot be reconstructed safely. Job status and each Goal run expose the accounting basis together with tokens used before the turn, remaining tokens, turn usage, and overshoot.
|
|
55
55
|
|
|
56
56
|
Set `--token-reserve <n>` to require more than `n` tokens to remain before Pibo starts another turn. Increase or clear the budget, or lower the reserve, before resuming a budget-limited Goal.
|
|
57
57
|
|