@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
|
@@ -1196,7 +1196,7 @@ export class PiboSessionRouter {
|
|
|
1196
1196
|
const status = current.getStatus();
|
|
1197
1197
|
if (status.disposed || status.processing || status.streaming || status.queuedMessages > 0)
|
|
1198
1198
|
return;
|
|
1199
|
-
await this.resetCachedSession(piboSessionId
|
|
1199
|
+
await this.resetCachedSession(piboSessionId);
|
|
1200
1200
|
}
|
|
1201
1201
|
async getOrCreateSession(piboSessionId) {
|
|
1202
1202
|
if (this.closing)
|
|
@@ -1391,6 +1391,7 @@ export class PiboSessionRouter {
|
|
|
1391
1391
|
...(runtimeBindingPersistence ? { runtimeBindingPersistence } : {}),
|
|
1392
1392
|
compatibility: {
|
|
1393
1393
|
persistSession: this.options.persistSession,
|
|
1394
|
+
piPackageStoreCwd: this.options.piPackageStoreCwd,
|
|
1394
1395
|
thinkingLevel: initialThinkingLevel ?? this.options.thinkingLevel,
|
|
1395
1396
|
retryDefaults: resolvePiboSessionRetryDefaults(piboSession.kind, this.options.retryDefaults),
|
|
1396
1397
|
extensionFactories: [
|
|
@@ -2015,6 +2016,8 @@ export class PiboSessionRouter {
|
|
|
2015
2016
|
resolveSettled = resolve;
|
|
2016
2017
|
});
|
|
2017
2018
|
const untrack = this.trackActiveSubagent(parentPiboSessionId, {
|
|
2019
|
+
agentId: child.id,
|
|
2020
|
+
requestId,
|
|
2018
2021
|
abortController: parentAbortController,
|
|
2019
2022
|
settled,
|
|
2020
2023
|
});
|
|
@@ -2098,8 +2101,14 @@ export class PiboSessionRouter {
|
|
|
2098
2101
|
const child = this.requireManagedAgent(parentPiboSessionId, agentId);
|
|
2099
2102
|
const ids = [agentId, ...this.descendantSessionIds(agentId)];
|
|
2100
2103
|
const idSet = new Set(ids);
|
|
2104
|
+
const activeParentRunIds = new Set([...(this.activeSubagentRequests.get(parentPiboSessionId) ?? [])]
|
|
2105
|
+
.filter((request) => idSet.has(request.agentId))
|
|
2106
|
+
.map((request) => request.requestId));
|
|
2101
2107
|
const cancellableRuns = this.runRegistry.listAll({ includeConsumed: true, includeDetached: true })
|
|
2102
|
-
.filter((run) =>
|
|
2108
|
+
.filter((run) => !isTerminalRunStatus(run.status) && (idSet.has(run.controllerPiboSessionId)
|
|
2109
|
+
|| (run.controllerPiboSessionId === parentPiboSessionId && activeParentRunIds.has(run.runId))));
|
|
2110
|
+
const reason = `killed by parent ${parentPiboSessionId}`;
|
|
2111
|
+
await this.cancelRunsAfterSettlement(cancellableRuns.filter((run) => run.controllerPiboSessionId === parentPiboSessionId), reason);
|
|
2103
2112
|
await Promise.allSettled(ids.flatMap((id) => this.sessions.has(id)
|
|
2104
2113
|
? [this.emit({ type: "execution", piboSessionId: id, action: "abort", id: randomUUID() })]
|
|
2105
2114
|
: []));
|
|
@@ -2112,7 +2121,7 @@ export class PiboSessionRouter {
|
|
|
2112
2121
|
},
|
|
2113
2122
|
});
|
|
2114
2123
|
}
|
|
2115
|
-
await this.disposeSessionSubtree(agentId,
|
|
2124
|
+
await this.disposeSessionSubtree(agentId, reason, { cancelRuns: true });
|
|
2116
2125
|
const cancelledRuns = cancellableRuns.flatMap((run) => {
|
|
2117
2126
|
const current = this.runRegistry.status(run.controllerPiboSessionId, run.runId);
|
|
2118
2127
|
return current.status === "cancelled" ? [run.runId] : [];
|
|
@@ -2368,9 +2377,11 @@ export class PiboSessionRouter {
|
|
|
2368
2377
|
: eventId
|
|
2369
2378
|
? this.subagentRequestIdsByEvent.get(subagentRequestEventKey(session.id, eventId))
|
|
2370
2379
|
: undefined;
|
|
2380
|
+
const sequence = this.sessionStore.claimAgentObservationSequence?.(session.parentId, this.nextAgentObservationSequence) ?? this.nextAgentObservationSequence;
|
|
2381
|
+
this.nextAgentObservationSequence = Math.max(this.nextAgentObservationSequence, sequence + 1);
|
|
2371
2382
|
const observation = {
|
|
2372
2383
|
managingParentId: session.parentId,
|
|
2373
|
-
sequence
|
|
2384
|
+
sequence,
|
|
2374
2385
|
createdAt: new Date().toISOString(),
|
|
2375
2386
|
...(requestId ? { requestId } : {}),
|
|
2376
2387
|
agentId: session.id,
|
package/dist/cron/cli.js
CHANGED
|
@@ -34,18 +34,44 @@ function maybeTargetFromOptions(options) {
|
|
|
34
34
|
return undefined;
|
|
35
35
|
return targetFromOptions(options);
|
|
36
36
|
}
|
|
37
|
+
function scheduleSelectors(options, positionalCron) {
|
|
38
|
+
const candidates = [
|
|
39
|
+
["--in", typeof options.in === "string"],
|
|
40
|
+
["--at", typeof options.at === "string"],
|
|
41
|
+
["--every", typeof options.every === "string"],
|
|
42
|
+
["--daily", typeof options.daily === "string"],
|
|
43
|
+
["--weekly", typeof options.weekly === "string"],
|
|
44
|
+
["--monthly", typeof options.monthly === "string" || typeof options.monthly === "number"],
|
|
45
|
+
["--cron", typeof options.cron === "string"],
|
|
46
|
+
["positional cron expression", Boolean(positionalCron)],
|
|
47
|
+
];
|
|
48
|
+
return candidates.filter(([, selected]) => selected).map(([selector]) => selector);
|
|
49
|
+
}
|
|
37
50
|
function hasScheduleOptions(options, positionalCron) {
|
|
38
|
-
return
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
51
|
+
return scheduleSelectors(options, positionalCron).length > 0;
|
|
52
|
+
}
|
|
53
|
+
function scheduleWithTimezone(job, tz) {
|
|
54
|
+
if (job.schedule.kind !== "cron")
|
|
55
|
+
throw new Error("--tz can only update an existing wall-clock cron schedule");
|
|
56
|
+
let scheduleUi;
|
|
57
|
+
if (job.scheduleUi) {
|
|
58
|
+
switch (job.scheduleUi.preset) {
|
|
59
|
+
case "daily":
|
|
60
|
+
case "weekly":
|
|
61
|
+
case "monthly":
|
|
62
|
+
case "advanced":
|
|
63
|
+
scheduleUi = { ...job.scheduleUi, tz };
|
|
64
|
+
break;
|
|
65
|
+
default:
|
|
66
|
+
scheduleUi = job.scheduleUi;
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
return { schedule: { ...job.schedule, tz }, scheduleUi };
|
|
47
70
|
}
|
|
48
71
|
function scheduleFromOptions(options, positionalCron) {
|
|
72
|
+
const selectors = scheduleSelectors(options, positionalCron);
|
|
73
|
+
if (selectors.length > 1)
|
|
74
|
+
throw new Error(`Conflicting schedule selectors: ${selectors.join(", ")}. Choose exactly one schedule source.`);
|
|
49
75
|
if (typeof options.in === "string")
|
|
50
76
|
return parseFriendlySchedule({ kind: "in", value: options.in });
|
|
51
77
|
if (typeof options.at === "string")
|
|
@@ -169,7 +195,13 @@ export async function runCronCli(argv = process.argv) {
|
|
|
169
195
|
.action((id, cronExprParts, options) => {
|
|
170
196
|
const store = createDefaultPiboCronStore({ path: program.opts().store });
|
|
171
197
|
const positionalCron = Array.isArray(cronExprParts) && cronExprParts.length ? cronExprParts.join(" ") : undefined;
|
|
172
|
-
|
|
198
|
+
let normalized = hasScheduleOptions(options, positionalCron) ? scheduleFromOptions(options, positionalCron) : undefined;
|
|
199
|
+
if (!normalized && typeof options.tz === "string") {
|
|
200
|
+
const existing = store.getJob(id);
|
|
201
|
+
if (!existing)
|
|
202
|
+
throw new Error("Cron job not found");
|
|
203
|
+
normalized = scheduleWithTimezone(existing, options.tz);
|
|
204
|
+
}
|
|
173
205
|
const target = maybeTargetFromOptions(options);
|
|
174
206
|
const patch = {
|
|
175
207
|
...(options.name !== undefined ? { name: options.name } : {}),
|
package/dist/cron/store.js
CHANGED
|
@@ -144,7 +144,7 @@ export class PiboCronStore {
|
|
|
144
144
|
runningAt: existing.state.runningAt,
|
|
145
145
|
nextRunAt: next.enabled ? computeNextRunAt(next.schedule, now)?.toISOString() : undefined,
|
|
146
146
|
};
|
|
147
|
-
if (next.enabled && !next.state.nextRunAt
|
|
147
|
+
if (next.enabled && !next.state.nextRunAt)
|
|
148
148
|
throw new Error("schedule has no future run");
|
|
149
149
|
this.writeJob(next);
|
|
150
150
|
return this.getJob(id);
|
package/dist/data/cli.js
CHANGED
|
@@ -86,15 +86,19 @@ function migrateSessionsToV2(input) {
|
|
|
86
86
|
if (!report.inputExists)
|
|
87
87
|
return report;
|
|
88
88
|
const source = new DatabaseSync(input.from, { readOnly: true });
|
|
89
|
-
|
|
89
|
+
let target;
|
|
90
90
|
try {
|
|
91
|
-
if (!hasTable(source, "pibo_sessions"))
|
|
92
|
-
|
|
91
|
+
if (!hasTable(source, "pibo_sessions")) {
|
|
92
|
+
throw new Error(`Cannot migrate sessions from "${input.from}": required legacy table "pibo_sessions" is missing.`);
|
|
93
|
+
}
|
|
94
|
+
target = new PiboDataStore(input.to);
|
|
93
95
|
const rows = source.prepare("SELECT * FROM pibo_sessions ORDER BY created_at ASC").all();
|
|
94
96
|
report.read = rows.length;
|
|
95
97
|
for (const row of rows) {
|
|
96
98
|
const existing = target.db.prepare("SELECT updated_at FROM sessions WHERE id = ?").get(row.id);
|
|
97
99
|
const metadata = parseJsonObject(row.metadata_json);
|
|
100
|
+
let migrateBinding = false;
|
|
101
|
+
const roomId = typeof metadata.chatRoomId === "string" ? metadata.chatRoomId : null;
|
|
98
102
|
const rootSessionId = row.parent_id ? (typeof metadata.rootSessionId === "string" ? metadata.rootSessionId : row.parent_id) : row.id;
|
|
99
103
|
if (!existing) {
|
|
100
104
|
const columns = [
|
|
@@ -105,22 +109,26 @@ function migrateSessionsToV2(input) {
|
|
|
105
109
|
target.db.prepare(`
|
|
106
110
|
INSERT INTO sessions (${columns.join(", ")})
|
|
107
111
|
VALUES (${columns.map(() => "?").join(", ")})
|
|
108
|
-
`).run(row.id, row.pi_session_id,
|
|
112
|
+
`).run(row.id, row.pi_session_id, roomId, rootSessionId, row.parent_id, row.origin_id, row.channel, row.kind, row.profile, row.active_model_json, row.workspace, row.title ?? "Untitled Session", "idle", JSON.stringify(metadata), row.created_at, row.updated_at, row.updated_at);
|
|
109
113
|
report.inserted++;
|
|
114
|
+
migrateBinding = true;
|
|
110
115
|
}
|
|
111
116
|
else if (row.updated_at > existing.updated_at) {
|
|
112
117
|
target.db.prepare(`
|
|
113
118
|
UPDATE sessions SET
|
|
114
|
-
pi_session_id = ?, root_session_id = ?, parent_id = ?, origin_id = ?,
|
|
119
|
+
pi_session_id = ?, room_id = ?, root_session_id = ?, parent_id = ?, origin_id = ?,
|
|
115
120
|
channel = ?, kind = ?, profile = ?, active_model_json = ?, workspace = ?, title = ?,
|
|
116
121
|
metadata_json = ?, updated_at = ?, last_activity_at = MAX(last_activity_at, ?)
|
|
117
122
|
WHERE id = ?
|
|
118
|
-
`).run(row.pi_session_id, rootSessionId, row.parent_id, row.origin_id, row.channel, row.kind, row.profile, row.active_model_json, row.workspace, row.title ?? "Untitled Session", JSON.stringify(metadata), row.updated_at, row.updated_at, row.id);
|
|
123
|
+
`).run(row.pi_session_id, roomId, rootSessionId, row.parent_id, row.origin_id, row.channel, row.kind, row.profile, row.active_model_json, row.workspace, row.title ?? "Untitled Session", JSON.stringify(metadata), row.updated_at, row.updated_at, row.id);
|
|
119
124
|
report.updated++;
|
|
125
|
+
migrateBinding = true;
|
|
120
126
|
}
|
|
121
127
|
else {
|
|
122
128
|
report.skipped++;
|
|
123
129
|
}
|
|
130
|
+
if (!migrateBinding)
|
|
131
|
+
continue;
|
|
124
132
|
target.db.prepare(`
|
|
125
133
|
INSERT INTO session_runtime_bindings (
|
|
126
134
|
pibo_session_id, runtime_instance_id, runtime_adapter_id, native_session_id,
|
|
@@ -142,7 +150,7 @@ function migrateSessionsToV2(input) {
|
|
|
142
150
|
}
|
|
143
151
|
finally {
|
|
144
152
|
source.close();
|
|
145
|
-
target
|
|
153
|
+
target?.close();
|
|
146
154
|
}
|
|
147
155
|
return report;
|
|
148
156
|
}
|
|
@@ -4,6 +4,22 @@ import { closeSync, existsSync, fstatSync, mkdirSync, openSync, readFileSync, re
|
|
|
4
4
|
import { dirname, extname, join, resolve } from "node:path";
|
|
5
5
|
import { piboHomePath } from "../core/pibo-home.js";
|
|
6
6
|
const MAX_SYNC_PAYLOAD_GZIP_BYTES = 512 * 1024;
|
|
7
|
+
export class PiboPayloadMetadataConflictError extends Error {
|
|
8
|
+
sha256;
|
|
9
|
+
existingContentType;
|
|
10
|
+
requestedContentType;
|
|
11
|
+
existingRetentionClass;
|
|
12
|
+
requestedRetentionClass;
|
|
13
|
+
constructor(sha256, existingContentType, requestedContentType, existingRetentionClass, requestedRetentionClass) {
|
|
14
|
+
super(`Payload bytes already exist with incompatible metadata: content type ${existingContentType} vs ${requestedContentType}; retention class ${existingRetentionClass} vs ${requestedRetentionClass}`);
|
|
15
|
+
this.sha256 = sha256;
|
|
16
|
+
this.existingContentType = existingContentType;
|
|
17
|
+
this.requestedContentType = requestedContentType;
|
|
18
|
+
this.existingRetentionClass = existingRetentionClass;
|
|
19
|
+
this.requestedRetentionClass = requestedRetentionClass;
|
|
20
|
+
this.name = "PiboPayloadMetadataConflictError";
|
|
21
|
+
}
|
|
22
|
+
}
|
|
7
23
|
export class PayloadStore {
|
|
8
24
|
db;
|
|
9
25
|
rootDir;
|
|
@@ -20,6 +36,9 @@ export class PayloadStore {
|
|
|
20
36
|
const sha256 = createHash("sha256").update(bytes).digest("hex");
|
|
21
37
|
const existing = this.findBySha256(sha256);
|
|
22
38
|
if (existing) {
|
|
39
|
+
if (existing.contentType !== contentType || existing.retentionClass !== input.retentionClass) {
|
|
40
|
+
throw new PiboPayloadMetadataConflictError(sha256, existing.contentType, contentType, existing.retentionClass, input.retentionClass);
|
|
41
|
+
}
|
|
23
42
|
this.db.prepare("UPDATE payloads SET ref_count = ref_count + 1 WHERE id = ?").run(existing.id);
|
|
24
43
|
return this.getPayload(existing.id) ?? existing;
|
|
25
44
|
}
|
|
@@ -111,6 +130,25 @@ export class PayloadStore {
|
|
|
111
130
|
const row = this.db.prepare("SELECT * FROM payloads WHERE sha256 = ?").get(sha256);
|
|
112
131
|
return row ? payloadFromRow(row) : undefined;
|
|
113
132
|
}
|
|
133
|
+
releaseReferences(id, count = 1) {
|
|
134
|
+
if (!Number.isSafeInteger(count) || count < 1)
|
|
135
|
+
throw new RangeError("count must be a positive safe integer");
|
|
136
|
+
const row = this.db.prepare("SELECT * FROM payloads WHERE id = ?").get(id);
|
|
137
|
+
if (!row)
|
|
138
|
+
return undefined;
|
|
139
|
+
if (row.ref_count > count) {
|
|
140
|
+
this.db.prepare("UPDATE payloads SET ref_count = ref_count - ? WHERE id = ?").run(count, id);
|
|
141
|
+
return undefined;
|
|
142
|
+
}
|
|
143
|
+
this.db.prepare("DELETE FROM payloads WHERE id = ?").run(id);
|
|
144
|
+
return payloadFromRow(row);
|
|
145
|
+
}
|
|
146
|
+
removeReleasedFile(payload) {
|
|
147
|
+
if (payload.storageKind !== "file" || !payload.storagePath)
|
|
148
|
+
return;
|
|
149
|
+
const absolutePath = this.rootDir === ":memory:" ? payload.storagePath : join(this.rootDir, payload.storagePath);
|
|
150
|
+
rmSync(absolutePath, { force: true });
|
|
151
|
+
}
|
|
114
152
|
}
|
|
115
153
|
function readFileBounded(path, maxBytes) {
|
|
116
154
|
const descriptor = openSync(path, "r");
|
package/dist/data/schema.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
export const PIBO_DATA_SCHEMA_VERSION = 7;
|
|
2
|
+
const NATIVE_HISTORY_FALLBACK_SCHEMA_VERSION = 5;
|
|
2
3
|
const retiredScopeColumn = ["owner", "scope"].join("_");
|
|
3
4
|
const retiredScopeTables = [
|
|
4
5
|
{
|
|
@@ -199,6 +200,13 @@ function applyPiboDataSchemaInTransaction(db, hooks, previousVersion, tablesToRe
|
|
|
199
200
|
FOREIGN KEY (pibo_session_id) REFERENCES sessions(id) ON DELETE CASCADE
|
|
200
201
|
);
|
|
201
202
|
|
|
203
|
+
CREATE TABLE IF NOT EXISTS session_agent_observation_counters (
|
|
204
|
+
parent_pibo_session_id TEXT PRIMARY KEY,
|
|
205
|
+
next_sequence INTEGER NOT NULL CHECK(next_sequence >= 1),
|
|
206
|
+
updated_at TEXT NOT NULL,
|
|
207
|
+
FOREIGN KEY (parent_pibo_session_id) REFERENCES sessions(id) ON DELETE CASCADE
|
|
208
|
+
);
|
|
209
|
+
|
|
202
210
|
CREATE TABLE IF NOT EXISTS session_output_part_counters (
|
|
203
211
|
pibo_session_id TEXT NOT NULL,
|
|
204
212
|
event_id TEXT NOT NULL,
|
|
@@ -784,7 +792,7 @@ function applyPiboDataSchemaInTransaction(db, hooks, previousVersion, tablesToRe
|
|
|
784
792
|
hooks.afterStep?.("sequence-repair-backfill");
|
|
785
793
|
db.exec("DROP TABLE pibo_v7_sequence_repair_sessions");
|
|
786
794
|
hooks.afterStep?.("sequence-repair-cleanup");
|
|
787
|
-
if (previousVersion <
|
|
795
|
+
if (previousVersion < NATIVE_HISTORY_FALLBACK_SCHEMA_VERSION && hadSessionsBeforeMigration) {
|
|
788
796
|
const rows = db.prepare("SELECT pibo_session_id, metadata_json FROM session_runtime_bindings").all();
|
|
789
797
|
const update = db.prepare("UPDATE session_runtime_bindings SET metadata_json = ? WHERE pibo_session_id = ?");
|
|
790
798
|
for (const row of rows) {
|
package/dist/debug/index.js
CHANGED
|
@@ -322,6 +322,11 @@ async function runDebugDb(args) {
|
|
|
322
322
|
const command = args[0];
|
|
323
323
|
const options = parseOptions(args.slice(1));
|
|
324
324
|
if (command === "stores") {
|
|
325
|
+
const unknownOption = options.positionals.find((value) => value.startsWith("-"));
|
|
326
|
+
if (unknownOption)
|
|
327
|
+
throw new Error(`Unknown option "${unknownOption}" for pibo debug db stores. Run pibo debug db --help.`);
|
|
328
|
+
if (options.positionals.length > 0)
|
|
329
|
+
throw new Error("pibo debug db stores accepts no positional arguments. Run pibo debug db --help.");
|
|
325
330
|
const stores = resolveDebugStores();
|
|
326
331
|
if (options.json) {
|
|
327
332
|
console.log(JSON.stringify({ stores }, null, 2));
|
package/dist/debug/pty.js
CHANGED
|
@@ -96,6 +96,26 @@ if exit_code is None:
|
|
|
96
96
|
exit_code = proc.wait(timeout=1)
|
|
97
97
|
sys.exit(exit_code if exit_code is not None else 1)
|
|
98
98
|
`;
|
|
99
|
+
const PTY_SCENARIO_FIELDS = new Set([
|
|
100
|
+
"name",
|
|
101
|
+
"command",
|
|
102
|
+
"cwd",
|
|
103
|
+
"workdir",
|
|
104
|
+
"env",
|
|
105
|
+
"rows",
|
|
106
|
+
"cols",
|
|
107
|
+
"timeoutMs",
|
|
108
|
+
"idleTimeoutMs",
|
|
109
|
+
"inputDelayMs",
|
|
110
|
+
"providerMode",
|
|
111
|
+
"maxIterations",
|
|
112
|
+
"artifactDir",
|
|
113
|
+
"artifact",
|
|
114
|
+
"steps",
|
|
115
|
+
"expect",
|
|
116
|
+
"reject",
|
|
117
|
+
"stopPatterns",
|
|
118
|
+
]);
|
|
99
119
|
class WallClockTimeoutError extends Error {
|
|
100
120
|
constructor(timeoutMs) {
|
|
101
121
|
super(`PTY command timed out after ${timeoutMs}ms`);
|
|
@@ -234,17 +254,17 @@ function parsePtyOptions(args) {
|
|
|
234
254
|
else if (arg === "--env")
|
|
235
255
|
options.env.push(requireOptionValue(args, ++index, arg));
|
|
236
256
|
else if (arg === "--expect")
|
|
237
|
-
options.expect.push(
|
|
257
|
+
options.expect.push(requireTextOptionValue(args, ++index, arg));
|
|
238
258
|
else if (arg === "--reject")
|
|
239
|
-
options.reject.push(
|
|
259
|
+
options.reject.push(requireTextOptionValue(args, ++index, arg));
|
|
240
260
|
else if (arg === "--stop-pattern")
|
|
241
|
-
options.stopPatterns.push(
|
|
261
|
+
options.stopPatterns.push(requireTextOptionValue(args, ++index, arg));
|
|
242
262
|
else if (arg === "--type")
|
|
243
|
-
options.steps.push({ typeText:
|
|
263
|
+
options.steps.push({ typeText: requireTextOptionValue(args, ++index, arg) });
|
|
244
264
|
else if (arg === "--press")
|
|
245
265
|
options.steps.push({ press: requireOptionValue(args, ++index, arg) });
|
|
246
266
|
else if (arg === "--wait-for")
|
|
247
|
-
options.steps.push({ waitFor:
|
|
267
|
+
options.steps.push({ waitFor: requireTextOptionValue(args, ++index, arg) });
|
|
248
268
|
else if (arg === "--sleep-ms")
|
|
249
269
|
options.steps.push({ sleepMs: parseNonNegativeInteger(requireOptionValue(args, ++index, arg), arg) });
|
|
250
270
|
else if (arg === "--builtin")
|
|
@@ -269,6 +289,12 @@ function requireOptionValue(args, index, option) {
|
|
|
269
289
|
throw new Error(`${option} requires a value`);
|
|
270
290
|
return value;
|
|
271
291
|
}
|
|
292
|
+
function requireTextOptionValue(args, index, option) {
|
|
293
|
+
const value = args[index];
|
|
294
|
+
if (value === undefined)
|
|
295
|
+
throw new Error(`${option} requires a value`);
|
|
296
|
+
return value;
|
|
297
|
+
}
|
|
272
298
|
function scenarioFromRunOptions(options) {
|
|
273
299
|
return {
|
|
274
300
|
name: options.name ?? "adhoc-run",
|
|
@@ -363,6 +389,10 @@ function validateScenario(value, source) {
|
|
|
363
389
|
if (!value || typeof value !== "object" || Array.isArray(value))
|
|
364
390
|
throw new Error(`${source}: scenario must be an object`);
|
|
365
391
|
const raw = value;
|
|
392
|
+
for (const field of Object.keys(raw)) {
|
|
393
|
+
if (!PTY_SCENARIO_FIELDS.has(field))
|
|
394
|
+
throw new Error(`${source}: unknown field "${field}"`);
|
|
395
|
+
}
|
|
366
396
|
if (!Array.isArray(raw.command) || raw.command.some((item) => typeof item !== "string") || raw.command.length === 0) {
|
|
367
397
|
throw new Error(`${source}: command must be a non-empty string array`);
|
|
368
398
|
}
|
|
@@ -425,6 +455,7 @@ async function executePtyScenario(input, options) {
|
|
|
425
455
|
let stopReason = "completed";
|
|
426
456
|
let exitCode = null;
|
|
427
457
|
let signal = null;
|
|
458
|
+
const waitCursor = { cleanOffset: 0 };
|
|
428
459
|
try {
|
|
429
460
|
validateProviderSafety(scenario, options);
|
|
430
461
|
runner = await startRunner(scenario);
|
|
@@ -449,7 +480,7 @@ async function executePtyScenario(input, options) {
|
|
|
449
480
|
}
|
|
450
481
|
}
|
|
451
482
|
await checkIdle(runner, refreshOutputTimestamp, scenario.idleTimeoutMs);
|
|
452
|
-
await runStep(runner, scenario, step, assertions, refreshOutputTimestamp, deadline);
|
|
483
|
+
await runStep(runner, scenario, step, assertions, refreshOutputTimestamp, deadline, waitCursor);
|
|
453
484
|
throwIfDeadlineExpired(deadline);
|
|
454
485
|
refreshOutputTimestamp();
|
|
455
486
|
const clean = cleanTerminalText(runner.getRawOutput());
|
|
@@ -667,9 +698,9 @@ function wrapChild(child, backend, ptyMethod) {
|
|
|
667
698
|
getEvents: () => events,
|
|
668
699
|
};
|
|
669
700
|
}
|
|
670
|
-
async function runStep(runner, scenario, step, assertions, markOutput, deadline) {
|
|
701
|
+
async function runStep(runner, scenario, step, assertions, markOutput, deadline, waitCursor) {
|
|
671
702
|
if (step.waitFor !== undefined) {
|
|
672
|
-
await waitForText(runner, step.waitFor, step.timeoutMs ?? scenario.timeoutMs, scenario.idleTimeoutMs, markOutput, deadline);
|
|
703
|
+
await waitForText(runner, step.waitFor, step.timeoutMs ?? scenario.timeoutMs, scenario.idleTimeoutMs, markOutput, deadline, waitCursor);
|
|
673
704
|
return;
|
|
674
705
|
}
|
|
675
706
|
if (step.typeText !== undefined) {
|
|
@@ -706,13 +737,17 @@ async function runStep(runner, scenario, step, assertions, markOutput, deadline)
|
|
|
706
737
|
if (step.reject !== undefined)
|
|
707
738
|
assertPatterns(clean, [], [step.reject], assertions);
|
|
708
739
|
}
|
|
709
|
-
async function waitForText(runner, pattern, timeoutMs, idleTimeoutMs, markOutput, globalDeadline) {
|
|
740
|
+
async function waitForText(runner, pattern, timeoutMs, idleTimeoutMs, markOutput, globalDeadline, cursor) {
|
|
710
741
|
const stepDeadline = performance.now() + timeoutMs;
|
|
711
742
|
while (true) {
|
|
712
743
|
throwIfDeadlineExpired(globalDeadline);
|
|
713
744
|
const lastOutputAt = markOutput();
|
|
714
|
-
|
|
745
|
+
const clean = cleanTerminalText(runner.getRawOutput());
|
|
746
|
+
const matchIndex = clean.indexOf(pattern, Math.min(cursor.cleanOffset, clean.length));
|
|
747
|
+
if (matchIndex >= 0) {
|
|
748
|
+
cursor.cleanOffset = matchIndex + pattern.length;
|
|
715
749
|
return;
|
|
750
|
+
}
|
|
716
751
|
const now = performance.now();
|
|
717
752
|
if (now - lastOutputAt > idleTimeoutMs) {
|
|
718
753
|
await runner.terminate("idle_timeout");
|
package/dist/gateway/cli.js
CHANGED
|
@@ -52,7 +52,19 @@ function expectedMode(target) {
|
|
|
52
52
|
}
|
|
53
53
|
function gatewayServiceName(target) {
|
|
54
54
|
const fromEnv = target === "web" ? process.env.PIBO_GATEWAY_WEB_SERVICE : process.env.PIBO_GATEWAY_DEV_SERVICE;
|
|
55
|
-
|
|
55
|
+
if (fromEnv)
|
|
56
|
+
return fromEnv;
|
|
57
|
+
if (target === "web") {
|
|
58
|
+
try {
|
|
59
|
+
const persisted = readFileSync(join(managedGatewayHome(target), "gateway-web-service"), "utf8").trim();
|
|
60
|
+
if (/^[A-Za-z0-9_.@-]+$/.test(persisted))
|
|
61
|
+
return persisted;
|
|
62
|
+
}
|
|
63
|
+
catch {
|
|
64
|
+
// Fall back to the default service name when setup has not persisted an override.
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
return target === "web" ? "pibo-web" : "pibo-web-dev";
|
|
56
68
|
}
|
|
57
69
|
function gatewayManagerCommand() {
|
|
58
70
|
return process.env.PIBO_GATEWAY_MANAGER_COMMAND || "systemctl";
|
|
@@ -62,7 +74,11 @@ function shouldUseWindowsGatewayManager() {
|
|
|
62
74
|
}
|
|
63
75
|
function managedGatewayHome(target) {
|
|
64
76
|
const fromEnv = target === "web" ? process.env.PIBO_GATEWAY_WEB_HOME : process.env.PIBO_GATEWAY_DEV_HOME;
|
|
65
|
-
|
|
77
|
+
if (fromEnv)
|
|
78
|
+
return fromEnv;
|
|
79
|
+
if (target === "web" && process.env.PIBO_HOME)
|
|
80
|
+
return process.env.PIBO_HOME;
|
|
81
|
+
return join(homedir(), target === "web" ? ".pibo" : ".pibo-dev");
|
|
66
82
|
}
|
|
67
83
|
function targetGatewayPort(target) {
|
|
68
84
|
const fromEnv = target === "web" ? process.env.PIBO_GATEWAY_WEB_AGENT_PORT : process.env.PIBO_GATEWAY_DEV_AGENT_PORT;
|
package/dist/gateway/server.js
CHANGED
|
@@ -159,6 +159,7 @@ export class PiboGatewayServer {
|
|
|
159
159
|
&& (this.options.persistSession !== false || hasExplicitPersistentStore);
|
|
160
160
|
this.router = new PiboSessionRouter({
|
|
161
161
|
persistSession: this.options.persistSession,
|
|
162
|
+
piPackageStoreCwd: this.options.piPackageStoreCwd ?? process.cwd(),
|
|
162
163
|
pluginRegistry: this.pluginRegistry,
|
|
163
164
|
sessionStore: this.sessionStore,
|
|
164
165
|
messagePreflight: createLoopMessagePreflight({ path: this.options.loopStorePath }),
|
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`.");
|