@pasko70/pibo 3.2.3 → 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-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 +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-Y6jYzsaC.js +0 -228
|
@@ -1,11 +1,9 @@
|
|
|
1
|
+
import { redactSensitiveText } from "../core/sensitive-data-redaction.js";
|
|
1
2
|
export const AGENT_RUNTIME_AUTH_METHOD_IDS = ["device_code", "browser_oauth", "api_key"];
|
|
2
3
|
export const AGENT_RUNTIME_AUTH_COMPLETION_MODES = ["immediate", "explicit", "notification"];
|
|
3
4
|
export function redactAgentRuntimeAuthText(value, maxLength = 1_000) {
|
|
4
|
-
return value
|
|
5
|
-
.replace(
|
|
6
|
-
.replace(/\b(?:sk|pk|ghp|github_pat|pibo)[-_][A-Za-z0-9_-]{8,}\b/g, "[redacted]")
|
|
7
|
-
.replace(/\beyJ[A-Za-z0-9_-]{10,}\.[A-Za-z0-9_-]{10,}\.[A-Za-z0-9_-]{10,}\b/g, "[redacted]")
|
|
8
|
-
.replace(/\b(access[_-]?token|refresh[_-]?token|id[_-]?token|api[_-]?key|token|secret|password|account[_-]?id)\b(\s*["']?\s*[:=]\s*["']?)([^\s"'&,}]+)/gi, "$1$2[redacted]")
|
|
5
|
+
return redactSensitiveText(value)
|
|
6
|
+
.replace(/\b(account[_-]?id)\b(\s*["']?\s*[:=]\s*["']?)([^\s"'&,}]+)/gi, "$1$2[redacted]")
|
|
9
7
|
.replace(/\b[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,}\b/gi, "[redacted account]")
|
|
10
8
|
.replace(/`(?:\/|[A-Za-z]:[\\/])[^`\r\n]+`/g, "`[redacted path]`")
|
|
11
9
|
.replace(/\bfile:\/\/\/?[^\s"'`,)]+/gi, "file://[redacted path]")
|
|
@@ -432,7 +432,7 @@ function boundEntries(entries, limits) {
|
|
|
432
432
|
const normalized = normalizeEntrySize(entry);
|
|
433
433
|
const size = entryBytes(normalized);
|
|
434
434
|
if (selected.length >= limits.maxEntries || bytes + size > limits.maxBytes)
|
|
435
|
-
|
|
435
|
+
break;
|
|
436
436
|
selected.push(normalized);
|
|
437
437
|
bytes += size;
|
|
438
438
|
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { spawn } from "node:child_process";
|
|
2
|
+
import { redactSensitiveText } from "../../core/sensitive-data-redaction.js";
|
|
2
3
|
const DEFAULT_STARTUP_TIMEOUT_MS = 10_000;
|
|
3
4
|
const DEFAULT_REQUEST_TIMEOUT_MS = 120_000;
|
|
4
5
|
const DEFAULT_SHUTDOWN_TIMEOUT_MS = 2_000;
|
|
@@ -92,11 +93,7 @@ function validateInitializeResponse(value) {
|
|
|
92
93
|
};
|
|
93
94
|
}
|
|
94
95
|
function redactCodexAppServerDiagnostic(value) {
|
|
95
|
-
return value
|
|
96
|
-
.replace(/Bearer\s+\S+/gi, "Bearer [redacted]")
|
|
97
|
-
.replace(/\b(?:sk|pk|ghp|github_pat|pibo)[-_][A-Za-z0-9_-]{8,}\b/g, "[redacted]")
|
|
98
|
-
.replace(/\beyJ[A-Za-z0-9_-]{10,}\.[A-Za-z0-9_-]{10,}\.[A-Za-z0-9_-]{10,}\b/g, "[redacted]")
|
|
99
|
-
.replace(/\b(access[_-]?token|refresh[_-]?token|id[_-]?token|api[_-]?key|token|secret|password)\b(\s*["']?\s*[:=]\s*["']?)([^\s"'&,}]+)/gi, "$1$2[redacted]")
|
|
96
|
+
return redactSensitiveText(value)
|
|
100
97
|
.replace(/`(?:\/|[A-Za-z]:[\\/])[^`\r\n]+`/g, "`[redacted path]`")
|
|
101
98
|
.replace(/\bfile:\/\/\/?[^\s"'`,)]+/gi, "file://[redacted path]")
|
|
102
99
|
.slice(0, 4_000);
|
|
@@ -1,31 +1,7 @@
|
|
|
1
|
-
|
|
1
|
+
import { redactSensitiveText, redactSensitiveValue } from "../../core/sensitive-data-redaction.js";
|
|
2
2
|
export function redactCodexNativeSensitiveText(value) {
|
|
3
|
-
return value
|
|
4
|
-
.replace(/Bearer\s+\S+/gi, "Bearer [redacted]")
|
|
5
|
-
.replace(/\b(?:sk|pk|ghp|github_pat|pibo)[-_][A-Za-z0-9_-]{8,}\b/g, "[redacted]")
|
|
6
|
-
.replace(/\beyJ[A-Za-z0-9_-]{10,}\.[A-Za-z0-9_-]{10,}\.[A-Za-z0-9_-]{10,}\b/g, "[redacted]")
|
|
7
|
-
.replace(/\b(access[_-]?token|refresh[_-]?token|id[_-]?token|api[_-]?key|token|secret|password)\b(\s*["']?\s*[:=]\s*["']?)([^\s"'&,}]+)/gi, "$1$2[redacted]");
|
|
3
|
+
return redactSensitiveText(value);
|
|
8
4
|
}
|
|
9
5
|
export function redactCodexNativeValue(value, key, depth = 0) {
|
|
10
|
-
|
|
11
|
-
return "[redacted]";
|
|
12
|
-
if (depth > 32)
|
|
13
|
-
return "[maximum depth reached]";
|
|
14
|
-
if (value === null || typeof value === "boolean")
|
|
15
|
-
return value;
|
|
16
|
-
if (typeof value === "string")
|
|
17
|
-
return redactCodexNativeSensitiveText(value);
|
|
18
|
-
if (typeof value === "number")
|
|
19
|
-
return Number.isFinite(value) ? value : String(value);
|
|
20
|
-
if (Array.isArray(value))
|
|
21
|
-
return value.map((entry) => redactCodexNativeValue(entry, undefined, depth + 1));
|
|
22
|
-
if (value && typeof value === "object") {
|
|
23
|
-
const result = {};
|
|
24
|
-
for (const [entryKey, entryValue] of Object.entries(value)) {
|
|
25
|
-
if (entryValue !== undefined)
|
|
26
|
-
result[entryKey] = redactCodexNativeValue(entryValue, entryKey, depth + 1);
|
|
27
|
-
}
|
|
28
|
-
return result;
|
|
29
|
-
}
|
|
30
|
-
return String(value);
|
|
6
|
+
return redactSensitiveValue(value, key, depth);
|
|
31
7
|
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { spawn } from "node:child_process";
|
|
2
|
+
import { redactSensitiveText } from "../../core/sensitive-data-redaction.js";
|
|
2
3
|
import { randomUUID } from "node:crypto";
|
|
3
4
|
import { OMP_RPC_CHUNK_PAYLOAD_BYTES, OMP_RPC_MAX_FRAME_BYTES, OMP_RPC_PROTOCOL_VERSION, } from "./protocol-types.js";
|
|
4
5
|
const DEFAULT_STARTUP_TIMEOUT_MS = 15_000;
|
|
@@ -109,10 +110,7 @@ export class OmpRpcClient {
|
|
|
109
110
|
* stderr (which may echo prompts or provider keys) cannot leak secrets.
|
|
110
111
|
*/
|
|
111
112
|
static redactDiagnostic(message) {
|
|
112
|
-
return message
|
|
113
|
-
.replace(/\b(?:sk|rk|pk|ghp|github_pat)[-_][A-Za-z0-9._~+/-]{8,}\b/g, "[redacted]")
|
|
114
|
-
.replace(/\bBearer\s+\S+/gi, "Bearer [redacted]")
|
|
115
|
-
.replace(/\b(?:api[_-]?key|secret|token|password)\b(?=[:=]\s*)\s*[^\s,;]+/gi, "$1 [redacted]");
|
|
113
|
+
return redactSensitiveText(message);
|
|
116
114
|
}
|
|
117
115
|
emitDiagnostic(message) {
|
|
118
116
|
for (const listener of this.diagnosticListeners)
|
|
@@ -635,6 +635,7 @@ class PiAgentRuntimeAdapter {
|
|
|
635
635
|
const intentTracing = piIntentTracingEnabled(profile.runtimeOptions);
|
|
636
636
|
const runtime = await createPiboRuntime({
|
|
637
637
|
cwd: input.workspace,
|
|
638
|
+
piPackageStoreCwd: compatibility?.piPackageStoreCwd,
|
|
638
639
|
persistSession: compatibility?.persistSession,
|
|
639
640
|
profile,
|
|
640
641
|
thinkingLevel: compatibility?.thinkingLevel,
|
|
@@ -223,7 +223,7 @@ export async function createPiboRuntime(options = {}) {
|
|
|
223
223
|
const skillPaths = options.resources
|
|
224
224
|
? [...options.resources.getSkillPaths("source")]
|
|
225
225
|
: getEnabledSkillPaths(runtimeCwd, profile);
|
|
226
|
-
const piPackageOptions = getPiPackageRuntimeOptions(runtimeCwd, profile);
|
|
226
|
+
const piPackageOptions = getPiPackageRuntimeOptions(options.piPackageStoreCwd ?? runtimeCwd, profile);
|
|
227
227
|
let runtimeSettingsManager;
|
|
228
228
|
const services = await createAgentSessionServices({
|
|
229
229
|
cwd: runtimeCwd,
|
|
@@ -62,7 +62,7 @@ export function previewCustomAgentUpdate(existing, input, options = {}) {
|
|
|
62
62
|
profileName,
|
|
63
63
|
displayName: input.displayName ?? existing.displayName,
|
|
64
64
|
folderId: input.folderId === undefined ? existing.folderId : input.folderId ?? undefined,
|
|
65
|
-
description: input.description === undefined ? existing.description : input.description,
|
|
65
|
+
description: input.description === undefined ? existing.description : input.description ?? undefined,
|
|
66
66
|
runtimeInstanceId: input.runtimeInstanceId === undefined ? existing.runtimeInstanceId : sanitizeRuntimeInstanceId(input.runtimeInstanceId),
|
|
67
67
|
runtimeOptions: input.runtimeOptions === undefined ? existing.runtimeOptions : cloneRuntimeOptions(input.runtimeOptions),
|
|
68
68
|
nativeSubagents: input.nativeSubagents === undefined ? existing.nativeSubagents : sanitizeBoolean(input.nativeSubagents),
|
|
@@ -6,7 +6,7 @@ import { protectPrivateDirectorySync, protectPrivateFileSync } from "../../core/
|
|
|
6
6
|
import { PiboWebHttpError } from "../../web/http.js";
|
|
7
7
|
import { imageMimeTypeFromBytes, TRACE_IMAGE_MAX_DECODED_BYTES } from "./trace-v2.js";
|
|
8
8
|
export const CHAT_UPLOAD_DIR = piboHomePath("uploads");
|
|
9
|
-
const CHAT_FILE_ATTACHMENT_LIMIT = 10;
|
|
9
|
+
export const CHAT_FILE_ATTACHMENT_LIMIT = 10;
|
|
10
10
|
export function prepareChatFileAttachments(input) {
|
|
11
11
|
const paths = normalizeChatFileAttachmentPaths(input.attachmentPaths);
|
|
12
12
|
if (!paths.length)
|
|
@@ -34,6 +34,8 @@ export async function saveUploadedChatFiles(request) {
|
|
|
34
34
|
}
|
|
35
35
|
if (!files.length)
|
|
36
36
|
throw new PiboWebHttpError("No files were uploaded", 400);
|
|
37
|
+
if (files.length > CHAT_FILE_ATTACHMENT_LIMIT)
|
|
38
|
+
throw new PiboWebHttpError(`At most ${CHAT_FILE_ATTACHMENT_LIMIT} uploaded files can be attached`, 400);
|
|
37
39
|
ensurePrivateChatUploadDirectory();
|
|
38
40
|
const saved = [];
|
|
39
41
|
for (const file of files) {
|
|
@@ -749,7 +749,7 @@ export function createAgentUpdate(body) {
|
|
|
749
749
|
if (body.displayName !== undefined)
|
|
750
750
|
update.displayName = normalizeAgentDisplayName(body.displayName);
|
|
751
751
|
if (body.description !== undefined)
|
|
752
|
-
update.description = normalizeAgentDescription(body.description);
|
|
752
|
+
update.description = normalizeAgentDescription(body.description) ?? null;
|
|
753
753
|
if (body.folderId !== undefined)
|
|
754
754
|
update.folderId = normalizeAgentFolderId(body.folderId);
|
|
755
755
|
if (body.runtimeInstanceId !== undefined)
|
|
@@ -170,6 +170,13 @@ export async function handleChatUserSkillRoute(options) {
|
|
|
170
170
|
const existing = userSkillManager.get(route.skillId, scope);
|
|
171
171
|
if (!existing)
|
|
172
172
|
throw new PiboWebHttpError("Skill not found", 404);
|
|
173
|
+
const hasEnabledReplacement = userSkillManager.list("all").some((skill) => (skill.id !== existing.id && skill.enabled && skill.name === existing.name));
|
|
174
|
+
const affectedAgents = existing.enabled && !hasEnabledReplacement
|
|
175
|
+
? options.agentsSelectingSkill(existing.name)
|
|
176
|
+
: [];
|
|
177
|
+
if (affectedAgents.length > 0) {
|
|
178
|
+
throw new PiboWebHttpError(`Skill is selected by custom agents: ${affectedAgents.map((agent) => agent.profileName).join(", ")}`, 409);
|
|
179
|
+
}
|
|
173
180
|
userSkillManager.remove(existing.id, scope);
|
|
174
181
|
syncAndInvalidate(options);
|
|
175
182
|
return responseJson({ removedSkillId: existing.id });
|
|
@@ -333,6 +333,24 @@ export class ChatProjectService {
|
|
|
333
333
|
const rows = this.db.prepare(`SELECT * FROM project_workflow_wait_tokens ${where} ORDER BY created_at DESC, id DESC LIMIT ?`).all(...values, limit);
|
|
334
334
|
return rows.map(projectWorkflowWaitTokenFromRow);
|
|
335
335
|
}
|
|
336
|
+
expireProjectWorkflowWaitToken(input) {
|
|
337
|
+
this.db.exec("BEGIN IMMEDIATE");
|
|
338
|
+
try {
|
|
339
|
+
const token = this.getProjectWorkflowWaitToken(input.waitTokenId);
|
|
340
|
+
if (!token)
|
|
341
|
+
throw new Error("Workflow wait token not found");
|
|
342
|
+
if (token.projectId !== input.projectId || token.piboSessionId !== input.piboSessionId || token.workflowRunId !== input.workflowRunId) {
|
|
343
|
+
throw new Error("Workflow wait token does not belong to this Project workflow session");
|
|
344
|
+
}
|
|
345
|
+
const result = this.expireProjectWorkflowWaitTokenLocked(token, input.resolvedAt ?? new Date().toISOString());
|
|
346
|
+
this.db.exec("COMMIT");
|
|
347
|
+
return result;
|
|
348
|
+
}
|
|
349
|
+
catch (error) {
|
|
350
|
+
this.db.exec("ROLLBACK");
|
|
351
|
+
throw error;
|
|
352
|
+
}
|
|
353
|
+
}
|
|
336
354
|
listProjectWorkflowHumanActions(filter = {}) {
|
|
337
355
|
const clauses = [];
|
|
338
356
|
const values = [];
|
|
@@ -370,7 +388,7 @@ export class ChatProjectService {
|
|
|
370
388
|
throw new Error(`Workflow wait token is ${token.status} and cannot be resolved again`);
|
|
371
389
|
const actedAt = input.actedAt ?? new Date().toISOString();
|
|
372
390
|
if (token.expiresAt && Date.parse(token.expiresAt) <= Date.parse(actedAt)) {
|
|
373
|
-
this.
|
|
391
|
+
this.expireProjectWorkflowWaitTokenLocked(token, actedAt);
|
|
374
392
|
this.db.exec("COMMIT");
|
|
375
393
|
throw new Error(`Workflow wait token expired at ${token.expiresAt}`);
|
|
376
394
|
}
|
|
@@ -494,6 +512,36 @@ export class ChatProjectService {
|
|
|
494
512
|
this.db.prepare("UPDATE project_sessions SET archived = ?, updated_at = ? WHERE pibo_session_id = ?").run(archived ? 1 : 0, now, piboSessionId);
|
|
495
513
|
return this.getProjectSession(piboSessionId);
|
|
496
514
|
}
|
|
515
|
+
expireProjectWorkflowWaitTokenLocked(token, resolvedAt) {
|
|
516
|
+
if (token.status !== "pending")
|
|
517
|
+
throw new Error(`Workflow wait token is ${token.status} and cannot be expired again`);
|
|
518
|
+
const resolvedAtMs = Date.parse(resolvedAt);
|
|
519
|
+
if (!Number.isFinite(resolvedAtMs))
|
|
520
|
+
throw new Error("Workflow wait token expiry resolution time is invalid");
|
|
521
|
+
if (!token.expiresAt || Date.parse(token.expiresAt) > resolvedAtMs)
|
|
522
|
+
throw new Error("Workflow wait token has not expired");
|
|
523
|
+
const pendingTokens = this.db.prepare(`SELECT id, expires_at FROM project_workflow_wait_tokens
|
|
524
|
+
WHERE project_id = ? AND pibo_session_id = ? AND workflow_run_id = ? AND status = 'pending'`)
|
|
525
|
+
.all(token.projectId, token.piboSessionId, token.workflowRunId);
|
|
526
|
+
const expire = this.db.prepare("UPDATE project_workflow_wait_tokens SET status = 'expired', resolved_at = ? WHERE id = ? AND status = 'pending'");
|
|
527
|
+
for (const pendingToken of pendingTokens) {
|
|
528
|
+
if (pendingToken.expires_at && Date.parse(pendingToken.expires_at) <= resolvedAtMs)
|
|
529
|
+
expire.run(resolvedAt, pendingToken.id);
|
|
530
|
+
}
|
|
531
|
+
const pending = this.db.prepare(`SELECT COUNT(*) AS count FROM project_workflow_wait_tokens
|
|
532
|
+
WHERE project_id = ? AND pibo_session_id = ? AND workflow_run_id = ? AND status = 'pending'`)
|
|
533
|
+
.get(token.projectId, token.piboSessionId, token.workflowRunId);
|
|
534
|
+
const nextStatus = pending.count === 0 ? "failed" : "waiting";
|
|
535
|
+
this.db.prepare("UPDATE project_workflow_runs SET status = ?, updated_at = ?, failed_at = CASE WHEN ? = 'failed' THEN COALESCE(failed_at, ?) ELSE failed_at END WHERE id = ? AND project_id = ? AND pibo_session_id = ? AND status IN ('running', 'waiting')")
|
|
536
|
+
.run(nextStatus, resolvedAt, nextStatus, resolvedAt, token.workflowRunId, token.projectId, token.piboSessionId);
|
|
537
|
+
this.db.prepare("UPDATE project_sessions SET state = ?, updated_at = ? WHERE pibo_session_id = ? AND project_id = ? AND state IN ('running', 'waiting')")
|
|
538
|
+
.run(nextStatus, resolvedAt, token.piboSessionId, token.projectId);
|
|
539
|
+
return {
|
|
540
|
+
waitToken: this.getProjectWorkflowWaitToken(token.id),
|
|
541
|
+
run: this.getProjectWorkflowRun(token.workflowRunId),
|
|
542
|
+
projectSession: this.getProjectSession(token.piboSessionId),
|
|
543
|
+
};
|
|
544
|
+
}
|
|
497
545
|
insertProjectWorkflowRunForExistingSession(row, input) {
|
|
498
546
|
const now = new Date().toISOString();
|
|
499
547
|
const run = {
|
|
@@ -77,12 +77,52 @@ export class ChatSessionQueryService {
|
|
|
77
77
|
if (!piboSessionIds.length)
|
|
78
78
|
return 0;
|
|
79
79
|
const placeholders = piboSessionIds.map(() => "?").join(", ");
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
this.store.
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
80
|
+
const payloadReleaseCounts = this.payloadReleaseCounts(placeholders, piboSessionIds);
|
|
81
|
+
const releasedPayloads = [];
|
|
82
|
+
const deleted = this.store.transaction(() => {
|
|
83
|
+
this.store.db.prepare(`DELETE FROM observations WHERE session_id IN (${placeholders})`).run(...piboSessionIds);
|
|
84
|
+
this.store.db.prepare(`DELETE FROM chat_messages WHERE session_id IN (${placeholders})`).run(...piboSessionIds);
|
|
85
|
+
this.store.db.prepare(`DELETE FROM event_log WHERE session_id IN (${placeholders})`).run(...piboSessionIds);
|
|
86
|
+
this.store.db.prepare(`DELETE FROM session_navigation WHERE session_id IN (${placeholders})`).run(...piboSessionIds);
|
|
87
|
+
for (const [payloadId, count] of payloadReleaseCounts) {
|
|
88
|
+
const released = this.store.payloads.releaseReferences(payloadId, count);
|
|
89
|
+
if (released)
|
|
90
|
+
releasedPayloads.push(released);
|
|
91
|
+
}
|
|
92
|
+
const result = this.store.db.prepare(`UPDATE sessions SET deleted_at = COALESCE(deleted_at, ?) WHERE id IN (${placeholders})`).run(new Date().toISOString(), ...piboSessionIds);
|
|
93
|
+
return Number(result.changes ?? 0);
|
|
94
|
+
});
|
|
95
|
+
for (const payload of releasedPayloads)
|
|
96
|
+
this.store.payloads.removeReleasedFile(payload);
|
|
97
|
+
return deleted;
|
|
98
|
+
}
|
|
99
|
+
payloadReleaseCounts(placeholders, piboSessionIds) {
|
|
100
|
+
const logicalReferences = new Map();
|
|
101
|
+
const add = (payloadId, key) => {
|
|
102
|
+
if (!payloadId)
|
|
103
|
+
return;
|
|
104
|
+
let references = logicalReferences.get(payloadId);
|
|
105
|
+
if (!references) {
|
|
106
|
+
references = new Set();
|
|
107
|
+
logicalReferences.set(payloadId, references);
|
|
108
|
+
}
|
|
109
|
+
references.add(key);
|
|
110
|
+
};
|
|
111
|
+
const events = this.store.db.prepare(`SELECT stream_id, payload_ref FROM event_log WHERE session_id IN (${placeholders}) AND payload_ref IS NOT NULL`).all(...piboSessionIds);
|
|
112
|
+
const eventPayloads = new Map(events.map((row) => [row.stream_id, row.payload_ref]));
|
|
113
|
+
for (const row of events)
|
|
114
|
+
add(row.payload_ref, `event:${row.stream_id}`);
|
|
115
|
+
const messages = this.store.db.prepare(`SELECT id, source_stream_id, content_payload_ref FROM chat_messages WHERE session_id IN (${placeholders}) AND content_payload_ref IS NOT NULL`).all(...piboSessionIds);
|
|
116
|
+
for (const row of messages) {
|
|
117
|
+
const linked = row.source_stream_id !== null && eventPayloads.get(row.source_stream_id) === row.content_payload_ref;
|
|
118
|
+
add(row.content_payload_ref, linked ? `event:${row.source_stream_id}` : `message:${row.id}`);
|
|
119
|
+
}
|
|
120
|
+
const observations = this.store.db.prepare(`SELECT id, event_stream_id, payload_ref FROM observations WHERE session_id IN (${placeholders}) AND payload_ref IS NOT NULL`).all(...piboSessionIds);
|
|
121
|
+
for (const row of observations) {
|
|
122
|
+
const linked = row.event_stream_id !== null && eventPayloads.get(row.event_stream_id) === row.payload_ref;
|
|
123
|
+
add(row.payload_ref, linked ? `event:${row.event_stream_id}` : `observation:${row.id}`);
|
|
124
|
+
}
|
|
125
|
+
return new Map([...logicalReferences].map(([payloadId, references]) => [payloadId, references.size]));
|
|
86
126
|
}
|
|
87
127
|
close() { }
|
|
88
128
|
sessionIndexMatches(session, status = "idle") {
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { PiboWebHttpError, readJsonBody, responseJson } from '../../web/http.js';
|
|
2
2
|
import { getPiboLoopService } from '../../loops/channel.js';
|
|
3
|
+
import { PiboLoopCapacityError } from '../../loops/service.js';
|
|
3
4
|
import { listLoopJobTemplates } from '../../loops/templates.js';
|
|
4
5
|
import { isPiboThinkingLevel } from '../../core/thinking.js';
|
|
5
6
|
import { normalizeLoopStopPolicy, PiboLoopActiveRunModeChangeError } from '../../loops/store.js';
|
|
@@ -140,7 +141,15 @@ export async function handleChatLoopApiRequest(options) {
|
|
|
140
141
|
if (!service)
|
|
141
142
|
throw new PiboWebHttpError('Loop service is not running', 503);
|
|
142
143
|
if (resource.child === 'start') {
|
|
143
|
-
|
|
144
|
+
let run;
|
|
145
|
+
try {
|
|
146
|
+
run = await service.startJob(resource.id);
|
|
147
|
+
}
|
|
148
|
+
catch (error) {
|
|
149
|
+
if (error instanceof PiboLoopCapacityError)
|
|
150
|
+
throw new PiboWebHttpError(error.message, 409);
|
|
151
|
+
throw error;
|
|
152
|
+
}
|
|
144
153
|
if (!run)
|
|
145
154
|
throw new PiboWebHttpError('Loop job not found, already running, or stopped by a before-run condition', 404);
|
|
146
155
|
return responseJson({ run: serializeRun(run) }, { status: 202 });
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { PiboWebHttpError, readJsonBody, responseJson } from '../../web/http.js';
|
|
2
2
|
import { getPiboRalphService } from '../../ralph/channel.js';
|
|
3
|
+
import { PiboRalphCapacityError } from '../../ralph/service.js';
|
|
3
4
|
import { listRalphJobTemplates } from '../../ralph/templates.js';
|
|
4
5
|
import { isPiboThinkingLevel } from '../../core/thinking.js';
|
|
5
6
|
import { normalizeRalphStopPolicy } from '../../ralph/store.js';
|
|
@@ -116,7 +117,15 @@ export async function handleChatRalphApiRequest(options) {
|
|
|
116
117
|
if (!service)
|
|
117
118
|
throw new PiboWebHttpError('Ralph service is not running', 503);
|
|
118
119
|
if (resource.child === 'start') {
|
|
119
|
-
|
|
120
|
+
let run;
|
|
121
|
+
try {
|
|
122
|
+
run = await service.startJob(resource.id);
|
|
123
|
+
}
|
|
124
|
+
catch (error) {
|
|
125
|
+
if (error instanceof PiboRalphCapacityError)
|
|
126
|
+
throw new PiboWebHttpError(error.message, 409);
|
|
127
|
+
throw error;
|
|
128
|
+
}
|
|
120
129
|
if (!run)
|
|
121
130
|
throw new PiboWebHttpError('Ralph job not found, already running, or stopped by a before-run condition', 404);
|
|
122
131
|
return responseJson({ run: serializeRun(run) }, { status: 202 });
|
|
@@ -2379,6 +2379,11 @@ function agentsSelectingPiPackage(state, packageId) {
|
|
|
2379
2379
|
.list({ includeArchived: true })
|
|
2380
2380
|
.filter((agent) => agent.piPackages.some((selected) => aliases.has(selected)));
|
|
2381
2381
|
}
|
|
2382
|
+
function agentsSelectingSkill(state, skillName) {
|
|
2383
|
+
return state.agentStore
|
|
2384
|
+
.list({ includeArchived: true })
|
|
2385
|
+
.filter((agent) => agent.skills.includes(skillName));
|
|
2386
|
+
}
|
|
2382
2387
|
function normalizeAgentDeleteConfirmation(value) {
|
|
2383
2388
|
if (typeof value !== "string" || value.trim().length === 0) {
|
|
2384
2389
|
throw new PiboWebHttpError("Type the agent name to permanently delete it.", 400);
|
|
@@ -2615,6 +2620,7 @@ async function buildContextBuildSnapshotForRequest(input) {
|
|
|
2615
2620
|
if (runtime.adapterId === "pi" && runtimeInfo.available) {
|
|
2616
2621
|
const snapshot = await inspectPiboContextBuild({
|
|
2617
2622
|
cwd,
|
|
2623
|
+
piPackageStoreCwd: input.piPackageStoreCwd,
|
|
2618
2624
|
profile,
|
|
2619
2625
|
activeModel: selectedSession.activeModel,
|
|
2620
2626
|
thinkingLevel: initialThinkingLevel,
|
|
@@ -3283,9 +3289,15 @@ function submitProjectWorkflowHumanAction(input) {
|
|
|
3283
3289
|
humanActionOptions: WORKFLOW_HUMAN_ACTION_REF_OPTIONS,
|
|
3284
3290
|
});
|
|
3285
3291
|
if (validation.diagnostics.length) {
|
|
3286
|
-
|
|
3287
|
-
input.state.projectService.
|
|
3288
|
-
|
|
3292
|
+
const responseWaitToken = waitToken && validation.expiredAt
|
|
3293
|
+
? input.state.projectService.expireProjectWorkflowWaitToken({
|
|
3294
|
+
projectId: project.id,
|
|
3295
|
+
piboSessionId: session.id,
|
|
3296
|
+
workflowRunId: projectSession.workflowRunId,
|
|
3297
|
+
waitTokenId: waitToken.id,
|
|
3298
|
+
resolvedAt: validation.checkedAt,
|
|
3299
|
+
}).waitToken
|
|
3300
|
+
: waitToken;
|
|
3289
3301
|
recordWorkflowLifecycleEvent(input.state, input.webSession, {
|
|
3290
3302
|
type: "workflow.human_action.submitted",
|
|
3291
3303
|
workflowId: projectSession.workflowId,
|
|
@@ -3297,7 +3309,7 @@ function submitProjectWorkflowHumanAction(input) {
|
|
|
3297
3309
|
diagnostics: validation.diagnostics,
|
|
3298
3310
|
payload: projectWorkflowHumanActionLifecyclePayload(request),
|
|
3299
3311
|
});
|
|
3300
|
-
return projectWorkflowHumanActionDiagnosticResponse("Human action was rejected by wait-token validation", validation.diagnostics, validation.httpStatus,
|
|
3312
|
+
return projectWorkflowHumanActionDiagnosticResponse("Human action was rejected by wait-token validation", validation.diagnostics, validation.httpStatus, responseWaitToken, WORKFLOW_HUMAN_ACTION_REF_OPTIONS);
|
|
3301
3313
|
}
|
|
3302
3314
|
try {
|
|
3303
3315
|
const result = input.state.projectService.resolveProjectWorkflowHumanAction({
|
|
@@ -3347,7 +3359,8 @@ function submitProjectWorkflowHumanAction(input) {
|
|
|
3347
3359
|
diagnostics,
|
|
3348
3360
|
payload: projectWorkflowHumanActionLifecyclePayload(request),
|
|
3349
3361
|
});
|
|
3350
|
-
|
|
3362
|
+
const responseWaitToken = input.state.projectService.getProjectWorkflowWaitToken(request.waitTokenId) ?? waitToken;
|
|
3363
|
+
return projectWorkflowHumanActionDiagnosticResponse("Human action was rejected by wait-token validation", diagnostics, 409, responseWaitToken, WORKFLOW_HUMAN_ACTION_REF_OPTIONS);
|
|
3351
3364
|
}
|
|
3352
3365
|
}
|
|
3353
3366
|
function isProjectWorkflowBackedSession(projectSession) {
|
|
@@ -3374,6 +3387,7 @@ async function sendProjectMessage(input) {
|
|
|
3374
3387
|
const projectSession = input.state.projectService.getProjectSession(selectedSession.id);
|
|
3375
3388
|
if (!projectSession)
|
|
3376
3389
|
throw new PiboWebHttpError("Project session not found", 404);
|
|
3390
|
+
requireSharedProject(input.state, input.webSession, projectSession.projectId);
|
|
3377
3391
|
const actorId = auditActorIdFor(input.webSession);
|
|
3378
3392
|
const duplicate = clientTxnId ? input.state.eventCommands.findByClientTxn(undefined, actorId, clientTxnId) : undefined;
|
|
3379
3393
|
if (duplicate)
|
|
@@ -3638,6 +3652,7 @@ async function sendChatMessage(input) {
|
|
|
3638
3652
|
}
|
|
3639
3653
|
export function createChatWebApp(options = {}) {
|
|
3640
3654
|
const integrations = resolveChatWebIntegrations(options);
|
|
3655
|
+
const piPackageStoreCwd = options.piPackageStoreCwd ?? process.cwd();
|
|
3641
3656
|
ensurePrivateChatUploadDirectory();
|
|
3642
3657
|
const defaultProfile = options.defaultProfile ?? "base";
|
|
3643
3658
|
const dataStore = createDataStore(options);
|
|
@@ -3934,6 +3949,7 @@ export function createChatWebApp(options = {}) {
|
|
|
3934
3949
|
context,
|
|
3935
3950
|
webSession,
|
|
3936
3951
|
piboSessionId: url.searchParams.get("piboSessionId") || undefined,
|
|
3952
|
+
piPackageStoreCwd,
|
|
3937
3953
|
});
|
|
3938
3954
|
return responseJson({ snapshot });
|
|
3939
3955
|
}
|
|
@@ -4691,6 +4707,7 @@ export function createChatWebApp(options = {}) {
|
|
|
4691
4707
|
state.syncedUserSkillNames = names;
|
|
4692
4708
|
},
|
|
4693
4709
|
invalidateBootstrapCatalogCache: () => invalidateBootstrapCatalogCache(state),
|
|
4710
|
+
agentsSelectingSkill: (skillName) => agentsSelectingSkill(state, skillName),
|
|
4694
4711
|
});
|
|
4695
4712
|
}
|
|
4696
4713
|
if (url.pathname === `${CHAT_WEB_API_PREFIX}/agent-folders` && request.method === "GET") {
|