@pasko70/pibo 3.0.2 → 3.1.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/compute-image/Dockerfile +47 -0
- package/compute-image/Dockerfile.dockerignore +5 -0
- package/dist/agent-runtime/contract.js +78 -28
- package/dist/agent-runtime/portable-history.js +19 -9
- package/dist/agent-runtime/registry.js +8 -2
- package/dist/agent-runtime/routed-session.js +12 -6
- package/dist/agent-runtimes/pi/routed-session.js +10 -4
- package/dist/apps/chat/agent-store.js +55 -3
- package/dist/apps/chat/data/project-service.js +119 -15
- package/dist/apps/chat/data/room-service.js +21 -0
- package/dist/apps/chat/loop-api.js +10 -2
- package/dist/apps/chat/web-app.js +84 -25
- package/dist/apps/chat-ui/assets/{dist-CvaPTBTN.js → dist-CUQJqggN.js} +1 -1
- package/dist/apps/chat-ui/assets/{dist-BPotHecb.js → dist-ClUlQWYN.js} +1 -1
- package/dist/apps/chat-ui/assets/{dist-BzqQKeVO.js → dist-Djul9BmZ.js} +1 -1
- package/dist/apps/chat-ui/assets/{dist-paagejNj.js → dist-GD0JGdCi.js} +1 -1
- package/dist/apps/chat-ui/assets/{dist-B6GZgWSj.js → dist-W5HOqHym.js} +1 -1
- package/dist/apps/chat-ui/assets/index-BcjkX-iP.js +228 -0
- package/dist/apps/chat-ui/assets/index-CmqRSbBU.css +1 -0
- package/dist/apps/chat-ui/index.html +2 -2
- package/dist/apps/chat-vscode-web/assets/index-JvrPUGvI.js +43 -0
- package/dist/apps/chat-vscode-web/index.html +1 -1
- package/dist/apps/cli-ui/InkSessionApp.js +111 -12
- package/dist/compute/cli.js +35 -16
- package/dist/compute/docker.js +341 -94
- package/dist/config/config.js +3 -0
- package/dist/core/output-render-sequence.js +148 -1
- package/dist/core/session-router.js +33 -105
- package/dist/data/ingest-service.js +14 -6
- package/dist/data/pibo-store.js +8 -1
- package/dist/data/schema.js +135 -5
- package/dist/debug/agents.js +91 -89
- package/dist/debug/index.js +2 -0
- package/dist/debug/pty.js +127 -48
- package/dist/gateway/cli.js +4 -0
- package/dist/loops/channel.js +3 -1
- package/dist/loops/cli.js +2 -1
- package/dist/loops/service.js +90 -11
- package/dist/loops/store.js +100 -74
- package/dist/mcp/agent-context.js +13 -26
- package/dist/mcp/commands/info.js +3 -1
- package/dist/mcp/config-command.js +46 -2
- package/dist/mcp/config.js +18 -4
- package/dist/plugins/context-files-store.js +6 -6
- package/dist/previews/base-url.js +34 -0
- package/dist/previews/config.js +2 -32
- package/dist/ralph/store.js +5 -0
- package/dist/reliability/store.js +174 -111
- package/dist/session-ui/terminalRows.js +2 -1
- package/dist/sessions/pibo-data-store.js +81 -0
- package/dist/subagents/observation-query.js +121 -0
- package/dist/tools/agent-browser-leases.js +58 -23
- package/dist/tools/agent-browser-wrapper.js +1 -1
- package/dist/tools/browser-use-leases.js +129 -16
- package/dist/tools/browser-use-wrapper.js +1 -1
- package/dist/tools/index.js +27 -11
- package/dist/tools/python-runtime.js +10 -3
- package/dist/tools/registry.js +1 -1
- package/dist/tools/runtime/node-backend.js +16 -5
- package/dist/tools/runtime/node-worker-source.js +112 -33
- package/dist/tools/runtime/python-backend.js +16 -5
- package/dist/tools/runtime/python-worker-source.js +167 -16
- package/dist/tools/runtime/registry.js +23 -10
- package/dist/user-skills/store.js +0 -1
- package/npm-shrinkwrap.json +9 -2
- package/package.json +8 -2
- package/scripts/docker-entrypoint.sh +46 -0
- package/scripts/prepare-agent-browser-wrapper.sh +70 -0
- package/scripts/prepare-browser-use-wrapper.sh +255 -0
- package/dist/apps/chat-ui/assets/index-6JJV-eic.js +0 -228
- package/dist/apps/chat-ui/assets/index-CeL9JPP5.css +0 -1
- package/dist/apps/chat-vscode-web/assets/index-U-MSErGa.js +0 -43
|
@@ -3,6 +3,7 @@ const RENDER_SEQUENCE_SLOTS_PER_MILLISECOND = 1_000;
|
|
|
3
3
|
const MAX_TRACKED_SESSIONS = 1_024;
|
|
4
4
|
const MAX_RECENT_SEGMENTS_PER_SESSION = 1_024;
|
|
5
5
|
const MAX_RECENT_TOOL_INVOCATIONS_PER_SESSION = 1_024;
|
|
6
|
+
const MAX_RECENT_OUTPUT_PARTS_PER_SESSION = 1_024;
|
|
6
7
|
export function outputRenderHighWaterStore(value) {
|
|
7
8
|
if (!value || typeof value !== "object")
|
|
8
9
|
return undefined;
|
|
@@ -32,7 +33,8 @@ export class OutputRenderSequencer {
|
|
|
32
33
|
position(event) {
|
|
33
34
|
const state = this.sessionState(event.piboSessionId);
|
|
34
35
|
this.trackActiveTurn(state, event);
|
|
35
|
-
const
|
|
36
|
+
const eventWithPartIdentity = this.positionOutputPart(state, event);
|
|
37
|
+
const eventWithToolIdentity = this.positionToolInvocation(state, eventWithPartIdentity);
|
|
36
38
|
const key = outputRenderSegmentKey(eventWithToolIdentity, state.activeEventId);
|
|
37
39
|
const supplied = validRenderSequence(eventWithToolIdentity.renderSequence)
|
|
38
40
|
? eventWithToolIdentity.renderSequence
|
|
@@ -90,11 +92,61 @@ export class OutputRenderSequencer {
|
|
|
90
92
|
activeSegments: new Set(),
|
|
91
93
|
completedSegments: new Set(),
|
|
92
94
|
toolInvocations: new Map(),
|
|
95
|
+
outputParts: new Map(),
|
|
93
96
|
lastSequence: 0,
|
|
94
97
|
};
|
|
95
98
|
this.sessions.set(piboSessionId, state);
|
|
96
99
|
return state;
|
|
97
100
|
}
|
|
101
|
+
positionOutputPart(state, event) {
|
|
102
|
+
const transition = outputPartTransition(event, state.activeEventId);
|
|
103
|
+
if (!transition)
|
|
104
|
+
return event;
|
|
105
|
+
const key = outputPartCounterKey(transition.eventId, transition.kind);
|
|
106
|
+
const parts = state.outputParts.get(key) ?? [];
|
|
107
|
+
if (!state.outputParts.has(key))
|
|
108
|
+
state.outputParts.set(key, parts);
|
|
109
|
+
const exact = parts.find((part) => part.fingerprints.has(transition.fingerprint));
|
|
110
|
+
const latestOpen = [...parts].reverse().find((part) => !part.closed);
|
|
111
|
+
const localMaximum = parts.reduce((maximum, part) => Math.max(maximum, part.index), -1);
|
|
112
|
+
const localIndex = exact?.index
|
|
113
|
+
?? latestOpen?.index
|
|
114
|
+
?? (transition.proposedIndex > localMaximum ? transition.proposedIndex : localMaximum + 1);
|
|
115
|
+
const index = validRenderSequence(event.renderSequence) || exact || latestOpen
|
|
116
|
+
? localIndex
|
|
117
|
+
: this.highWaterStore?.claimOrAttachOutputPart?.({ ...transition, proposedIndex: localIndex }) ?? localIndex;
|
|
118
|
+
let part = parts.find((candidate) => candidate.index === index);
|
|
119
|
+
if (!part) {
|
|
120
|
+
part = { index, fingerprints: new Set(), closed: false };
|
|
121
|
+
parts.push(part);
|
|
122
|
+
}
|
|
123
|
+
part.fingerprints.add(transition.fingerprint);
|
|
124
|
+
if (transition.terminal)
|
|
125
|
+
part.closed = true;
|
|
126
|
+
this.trimOutputParts(state);
|
|
127
|
+
return withOutputPartIndex(event, transition.kind, index);
|
|
128
|
+
}
|
|
129
|
+
trimOutputParts(state) {
|
|
130
|
+
let total = 0;
|
|
131
|
+
for (const parts of state.outputParts.values())
|
|
132
|
+
total += parts.length;
|
|
133
|
+
while (total > MAX_RECENT_OUTPUT_PARTS_PER_SESSION) {
|
|
134
|
+
let removed = false;
|
|
135
|
+
for (const [key, parts] of state.outputParts) {
|
|
136
|
+
const closedIndex = parts.findIndex((part) => part.closed);
|
|
137
|
+
if (closedIndex === -1)
|
|
138
|
+
continue;
|
|
139
|
+
parts.splice(closedIndex, 1);
|
|
140
|
+
total -= 1;
|
|
141
|
+
if (!parts.length)
|
|
142
|
+
state.outputParts.delete(key);
|
|
143
|
+
removed = true;
|
|
144
|
+
break;
|
|
145
|
+
}
|
|
146
|
+
if (!removed)
|
|
147
|
+
break;
|
|
148
|
+
}
|
|
149
|
+
}
|
|
98
150
|
positionToolInvocation(state, event) {
|
|
99
151
|
if (!isToolIdentityEvent(event) || !event.toolCallId)
|
|
100
152
|
return event;
|
|
@@ -243,6 +295,101 @@ function createInvocation(invocations, ordinal) {
|
|
|
243
295
|
invocations.push(invocation);
|
|
244
296
|
return invocation;
|
|
245
297
|
}
|
|
298
|
+
function outputPartTransition(event, activeEventId) {
|
|
299
|
+
const eventId = ("eventId" in event ? event.eventId : undefined) ?? activeEventId;
|
|
300
|
+
if (!eventId)
|
|
301
|
+
return undefined;
|
|
302
|
+
if (event.type === "assistant_delta" || event.type === "assistant_message") {
|
|
303
|
+
return {
|
|
304
|
+
piboSessionId: event.piboSessionId,
|
|
305
|
+
eventId,
|
|
306
|
+
kind: "assistant",
|
|
307
|
+
proposedIndex: event.assistantIndex ?? event.contentIndex ?? 0,
|
|
308
|
+
fingerprint: outputPartFingerprint(event),
|
|
309
|
+
identityFingerprint: outputIdentityFingerprint(event),
|
|
310
|
+
terminal: event.type === "assistant_message",
|
|
311
|
+
};
|
|
312
|
+
}
|
|
313
|
+
if (event.type === "thinking_started" || event.type === "thinking_delta" || event.type === "thinking_finished") {
|
|
314
|
+
return {
|
|
315
|
+
piboSessionId: event.piboSessionId,
|
|
316
|
+
eventId,
|
|
317
|
+
kind: "thinking",
|
|
318
|
+
proposedIndex: event.thinkingIndex ?? event.contentIndex ?? 0,
|
|
319
|
+
fingerprint: outputPartFingerprint(event),
|
|
320
|
+
identityFingerprint: outputIdentityFingerprint(event),
|
|
321
|
+
terminal: event.type === "thinking_finished",
|
|
322
|
+
};
|
|
323
|
+
}
|
|
324
|
+
if (event.type === "assistant_usage") {
|
|
325
|
+
return {
|
|
326
|
+
piboSessionId: event.piboSessionId,
|
|
327
|
+
eventId,
|
|
328
|
+
kind: "usage",
|
|
329
|
+
proposedIndex: event.usageIndex ?? 0,
|
|
330
|
+
fingerprint: outputPartFingerprint(event),
|
|
331
|
+
identityFingerprint: outputIdentityFingerprint(event),
|
|
332
|
+
terminal: true,
|
|
333
|
+
};
|
|
334
|
+
}
|
|
335
|
+
if (event.type === "compaction_start" || event.type === "compaction_end") {
|
|
336
|
+
return {
|
|
337
|
+
piboSessionId: event.piboSessionId,
|
|
338
|
+
eventId,
|
|
339
|
+
kind: "compaction",
|
|
340
|
+
proposedIndex: event.compactionIndex ?? 0,
|
|
341
|
+
fingerprint: outputPartFingerprint(event),
|
|
342
|
+
identityFingerprint: outputIdentityFingerprint(event),
|
|
343
|
+
terminal: event.type === "compaction_end",
|
|
344
|
+
};
|
|
345
|
+
}
|
|
346
|
+
return undefined;
|
|
347
|
+
}
|
|
348
|
+
export function outputIdentityFingerprint(event) {
|
|
349
|
+
const payload = { ...event };
|
|
350
|
+
delete payload.renderSequence;
|
|
351
|
+
return createHash("sha256").update(stableJson(payload)).digest("hex");
|
|
352
|
+
}
|
|
353
|
+
export function outputPartFingerprint(event) {
|
|
354
|
+
const payload = { ...event };
|
|
355
|
+
delete payload.renderSequence;
|
|
356
|
+
delete payload.assistantIndex;
|
|
357
|
+
delete payload.thinkingIndex;
|
|
358
|
+
delete payload.usageIndex;
|
|
359
|
+
delete payload.compactionIndex;
|
|
360
|
+
delete payload.contentIndex;
|
|
361
|
+
return createHash("sha256").update(stableJson(payload)).digest("hex");
|
|
362
|
+
}
|
|
363
|
+
function stableJson(value) {
|
|
364
|
+
if (Array.isArray(value))
|
|
365
|
+
return `[${value.map(stableJson).join(",")}]`;
|
|
366
|
+
if (value && typeof value === "object") {
|
|
367
|
+
return `{${Object.entries(value)
|
|
368
|
+
.filter(([, entry]) => entry !== undefined)
|
|
369
|
+
.sort(([left], [right]) => left.localeCompare(right))
|
|
370
|
+
.map(([key, entry]) => `${JSON.stringify(key)}:${stableJson(entry)}`)
|
|
371
|
+
.join(",")}}`;
|
|
372
|
+
}
|
|
373
|
+
return JSON.stringify(value) ?? "null";
|
|
374
|
+
}
|
|
375
|
+
function outputPartCounterKey(eventId, kind) {
|
|
376
|
+
return JSON.stringify([eventId, kind]);
|
|
377
|
+
}
|
|
378
|
+
function withOutputPartIndex(event, kind, index) {
|
|
379
|
+
if (kind === "assistant" && (event.type === "assistant_delta" || event.type === "assistant_message")) {
|
|
380
|
+
return event.assistantIndex === index ? event : { ...event, assistantIndex: index };
|
|
381
|
+
}
|
|
382
|
+
if (kind === "thinking" && (event.type === "thinking_started" || event.type === "thinking_delta" || event.type === "thinking_finished")) {
|
|
383
|
+
return event.thinkingIndex === index ? event : { ...event, thinkingIndex: index };
|
|
384
|
+
}
|
|
385
|
+
if (kind === "usage" && event.type === "assistant_usage") {
|
|
386
|
+
return event.usageIndex === index ? event : { ...event, usageIndex: index };
|
|
387
|
+
}
|
|
388
|
+
if (kind === "compaction" && (event.type === "compaction_start" || event.type === "compaction_end")) {
|
|
389
|
+
return event.compactionIndex === index ? event : { ...event, compactionIndex: index };
|
|
390
|
+
}
|
|
391
|
+
return event;
|
|
392
|
+
}
|
|
246
393
|
function segmentRemainsActive(event) {
|
|
247
394
|
return event.type === "message_started"
|
|
248
395
|
|| event.type === "assistant_delta"
|
|
@@ -2,11 +2,12 @@ import { randomUUID } from "node:crypto";
|
|
|
2
2
|
import { isDeepStrictEqual } from "node:util";
|
|
3
3
|
import { InitialSessionContext, } from "./profiles.js";
|
|
4
4
|
import { createDefaultPiboPluginRegistry, createPiboProfileFromRegistryOrDefault, resolvePiboProfileNameFromRegistryOrDefault, selectDefaultPiboProfileName } from "../plugins/builtin.js";
|
|
5
|
-
import { RuntimeRoutedSession as RoutedSession, } from "../agent-runtime/routed-session.js";
|
|
5
|
+
import { RUN_REMINDER_MAX_DURATION_MS, RuntimeRoutedSession as RoutedSession, } from "../agent-runtime/routed-session.js";
|
|
6
6
|
import { runtimeSessionErrorDetails } from "./session-errors.js";
|
|
7
7
|
import { OutputRenderSequencer, outputRenderHighWaterStore } from "./output-render-sequence.js";
|
|
8
8
|
import { normalizePiboAgentSessionName, } from "../subagents/tool.js";
|
|
9
|
-
import {
|
|
9
|
+
import { piboAgentObservationDetails, piboAgentObservationKind, piboAgentObservationRole, piboAgentObservationSourceFromEvent, piboAgentObservationText, } from "../subagents/observations.js";
|
|
10
|
+
import { preparePiboAgentObservationQuery, selectPiboAgentObservationPage, } from "../subagents/observation-query.js";
|
|
10
11
|
import { PiboRunRegistry } from "../runs/registry.js";
|
|
11
12
|
import { PiboRunCancellationError, PiboRunCancelledError, PiboRunExecutionTimeoutError, waitForRunCancellationSettlement } from "../runs/lifecycle.js";
|
|
12
13
|
import { PiboRunResourceLimitError } from "../runs/resource-isolation.js";
|
|
@@ -159,7 +160,11 @@ function formatRunReminderMessage(notification) {
|
|
|
159
160
|
toolName: run.toolName,
|
|
160
161
|
summary: run.summary,
|
|
161
162
|
})),
|
|
162
|
-
instruction:
|
|
163
|
+
instruction: [
|
|
164
|
+
`This autonomous run-reminder turn stops after ${RUN_REMINDER_MAX_DURATION_MS / 60_000} minutes of wall-clock time.`,
|
|
165
|
+
"Handle the listed runs promptly, then finish the turn. Do not start new subagents, yielded runs, or other long-running work from this reminder; leave larger follow-up work for a separate user-initiated or Goal continuation turn.",
|
|
166
|
+
"Use pibo_run_read for completed, failed, or timed_out runs. Use pibo_run_wait, pibo_run_status, pibo_run_cancel, or pibo_run_ack for runs you still need to manage.",
|
|
167
|
+
].join(" "),
|
|
163
168
|
}),
|
|
164
169
|
"</pibo_run_notification>",
|
|
165
170
|
].join("\n");
|
|
@@ -425,9 +430,15 @@ export class PiboSessionRouter {
|
|
|
425
430
|
this.signalRegistry.project({ type: "session_created", session });
|
|
426
431
|
this.signalRegistry.project({ type: "pibo_output", event: recovery.event });
|
|
427
432
|
}
|
|
433
|
+
const recoveredRunReminderControllers = new Set();
|
|
428
434
|
for (const run of this.runRegistry.listAll({ includeConsumed: true, includeDetached: true })) {
|
|
429
435
|
this.signalRegistry.project({ type: "run_changed", run, reason: "recovered" });
|
|
436
|
+
if (options.recoverInterruptedRuntimeState && isTerminalRunStatus(run.status) && this.sessionStore.get(run.controllerPiboSessionId)) {
|
|
437
|
+
recoveredRunReminderControllers.add(run.controllerPiboSessionId);
|
|
438
|
+
}
|
|
430
439
|
}
|
|
440
|
+
for (const piboSessionId of recoveredRunReminderControllers)
|
|
441
|
+
this.scheduleRunReminder(piboSessionId, false);
|
|
431
442
|
}
|
|
432
443
|
subscribe(listener) {
|
|
433
444
|
this.listeners.add(listener);
|
|
@@ -923,8 +934,12 @@ export class PiboSessionRouter {
|
|
|
923
934
|
expectedRevision: input.expectedRevision,
|
|
924
935
|
mode,
|
|
925
936
|
});
|
|
926
|
-
if (switchingRuntime)
|
|
927
|
-
this.sessionStore.update(piboSessionId, {
|
|
937
|
+
if (switchingRuntime) {
|
|
938
|
+
this.sessionStore.update(piboSessionId, {
|
|
939
|
+
activeModel: null,
|
|
940
|
+
metadata: withPiboSessionModelFallbacksMetadata(session.metadata, []),
|
|
941
|
+
});
|
|
942
|
+
}
|
|
928
943
|
return structuredClone(persisted);
|
|
929
944
|
}
|
|
930
945
|
getSessionRuntimeStatus(piboSessionId) {
|
|
@@ -2069,105 +2084,15 @@ export class PiboSessionRouter {
|
|
|
2069
2084
|
return session;
|
|
2070
2085
|
}
|
|
2071
2086
|
observeManagedAgents(parentPiboSessionId, input) {
|
|
2072
|
-
const
|
|
2073
|
-
|
|
2074
|
-
const
|
|
2075
|
-
const
|
|
2076
|
-
|
|
2077
|
-
|
|
2078
|
-
|
|
2079
|
-
|
|
2080
|
-
|
|
2081
|
-
const requestIds = input.requestIds ? new Set(input.requestIds) : undefined;
|
|
2082
|
-
if (input.toolCallIds && input.toolCallIds.length > 50) {
|
|
2083
|
-
throw new Error("Agent observation toolCallIds must contain at most 50 entries.");
|
|
2084
|
-
}
|
|
2085
|
-
const toolCallIds = input.toolCallIds ? new Set(input.toolCallIds) : undefined;
|
|
2086
|
-
const agentIds = input.agentIds ? new Set(input.agentIds) : undefined;
|
|
2087
|
-
if (agentIds) {
|
|
2088
|
-
for (const agentId of agentIds)
|
|
2089
|
-
this.requireManagedAgent(parentPiboSessionId, agentId);
|
|
2090
|
-
}
|
|
2091
|
-
const names = input.names ? new Set(input.names) : undefined;
|
|
2092
|
-
const threadKeys = input.threadKeys ? new Set(input.threadKeys) : undefined;
|
|
2093
|
-
const eventTypes = input.eventTypes ? new Set(input.eventTypes) : undefined;
|
|
2094
|
-
const kinds = input.kinds ? new Set(input.kinds) : undefined;
|
|
2095
|
-
const roles = input.roles ? new Set(input.roles) : undefined;
|
|
2096
|
-
const textContains = input.textContains?.toLowerCase();
|
|
2097
|
-
const defaultMessageView = eventTypes === undefined && kinds === undefined;
|
|
2098
|
-
const explicitlySelectsTools = input.eventTypes?.some((eventType) => piboAgentObservationKind(eventType) === "tool") === true
|
|
2099
|
-
|| input.kinds?.includes("tool") === true
|
|
2100
|
-
|| toolCallIds !== undefined;
|
|
2101
|
-
const includeTools = input.includeTools === true || (input.includeTools === undefined && explicitlySelectsTools);
|
|
2102
|
-
const defaultEventTypes = includeTools
|
|
2103
|
-
? [...PIBO_AGENT_OBSERVATION_DEFAULT_EVENT_TYPES, ...PIBO_AGENT_OBSERVATION_DEFAULT_TOOL_EVENT_TYPES]
|
|
2104
|
-
: [...PIBO_AGENT_OBSERVATION_DEFAULT_EVENT_TYPES];
|
|
2105
|
-
const defaultEventTypeSet = new Set(defaultEventTypes);
|
|
2106
|
-
const matches = this.agentObservations.filter((observation) => {
|
|
2107
|
-
if (observation.managingParentId !== parentPiboSessionId)
|
|
2108
|
-
return false;
|
|
2109
|
-
if (requestIds && (!observation.requestId || !requestIds.has(observation.requestId)))
|
|
2110
|
-
return false;
|
|
2111
|
-
if (toolCallIds && (!observation.toolCallId || !toolCallIds.has(observation.toolCallId)))
|
|
2112
|
-
return false;
|
|
2113
|
-
if (agentIds && !agentIds.has(observation.agentId))
|
|
2114
|
-
return false;
|
|
2115
|
-
if (names && !names.has(observation.name))
|
|
2116
|
-
return false;
|
|
2117
|
-
if (threadKeys && (!observation.threadKey || !threadKeys.has(observation.threadKey)))
|
|
2118
|
-
return false;
|
|
2119
|
-
if (observation.kind === "tool" && !includeTools)
|
|
2120
|
-
return false;
|
|
2121
|
-
if (defaultMessageView && !defaultEventTypeSet.has(observation.eventType))
|
|
2122
|
-
return false;
|
|
2123
|
-
if (eventTypes && !eventTypes.has(observation.eventType))
|
|
2124
|
-
return false;
|
|
2125
|
-
if (kinds && !kinds.has(observation.kind))
|
|
2126
|
-
return false;
|
|
2127
|
-
if (roles && (!observation.role || !roles.has(observation.role)))
|
|
2128
|
-
return false;
|
|
2129
|
-
if (afterSequence !== undefined && observation.sequence <= afterSequence)
|
|
2130
|
-
return false;
|
|
2131
|
-
const createdAt = Date.parse(observation.createdAt);
|
|
2132
|
-
if (since !== undefined && createdAt < since)
|
|
2133
|
-
return false;
|
|
2134
|
-
if (until !== undefined && createdAt > until)
|
|
2135
|
-
return false;
|
|
2136
|
-
if (textContains && !(observation.text ?? "").toLowerCase().includes(textContains))
|
|
2137
|
-
return false;
|
|
2138
|
-
return true;
|
|
2139
|
-
});
|
|
2140
|
-
const cursorPolling = afterSequence !== undefined;
|
|
2141
|
-
const ordered = cursorPolling
|
|
2142
|
-
? matches.slice(0, limit)
|
|
2143
|
-
: [...matches].sort((left, right) => order === "asc" ? left.sequence - right.sequence : right.sequence - left.sequence).slice(0, limit);
|
|
2144
|
-
if (cursorPolling && order === "desc")
|
|
2145
|
-
ordered.reverse();
|
|
2146
|
-
const selected = ordered.map((observation) => {
|
|
2147
|
-
const { managingParentId: _managingParentId, details, ...visible } = observation;
|
|
2148
|
-
const compact = visible.kind === "tool" && toolDetail === "summary"
|
|
2149
|
-
? { ...visible, text: piboAgentObservationToolSummary(visible.text, visible.isError, details) }
|
|
2150
|
-
: visible;
|
|
2151
|
-
return input.includeDetails === true && details !== undefined ? { ...compact, details } : compact;
|
|
2152
|
-
});
|
|
2153
|
-
const evictedThrough = this.agentObservationEvictedThroughByParent.get(parentPiboSessionId) ?? 0;
|
|
2154
|
-
const retentionTruncated = afterSequence === undefined ? evictedThrough > 0 : afterSequence < evictedThrough;
|
|
2155
|
-
const nextAfterSequence = selected.reduce((maximum, observation) => Math.max(maximum, observation.sequence), afterSequence === undefined ? 0 : Math.max(afterSequence, evictedThrough));
|
|
2156
|
-
return {
|
|
2157
|
-
filters: {
|
|
2158
|
-
...input,
|
|
2159
|
-
...(defaultMessageView ? { eventTypes: defaultEventTypes } : {}),
|
|
2160
|
-
...(afterSequence !== undefined ? { afterSequence } : {}),
|
|
2161
|
-
order,
|
|
2162
|
-
limit,
|
|
2163
|
-
includeTools,
|
|
2164
|
-
toolDetail,
|
|
2165
|
-
includeDetails: input.includeDetails === true,
|
|
2166
|
-
},
|
|
2167
|
-
observations: selected,
|
|
2168
|
-
nextAfterSequence,
|
|
2169
|
-
truncated: retentionTruncated || matches.length > selected.length,
|
|
2170
|
-
};
|
|
2087
|
+
for (const agentId of input.agentIds ?? [])
|
|
2088
|
+
this.requireManagedAgent(parentPiboSessionId, agentId);
|
|
2089
|
+
const query = preparePiboAgentObservationQuery(input);
|
|
2090
|
+
const ordered = query.scanOrder === "asc"
|
|
2091
|
+
? this.agentObservations
|
|
2092
|
+
: [...this.agentObservations].reverse();
|
|
2093
|
+
return selectPiboAgentObservationPage(ordered
|
|
2094
|
+
.filter((observation) => observation.managingParentId === parentPiboSessionId)
|
|
2095
|
+
.map(({ managingParentId: _managingParentId, ...observation }) => observation), query, { evictedThrough: this.agentObservationEvictedThroughByParent.get(parentPiboSessionId) ?? 0 });
|
|
2171
2096
|
}
|
|
2172
2097
|
async killManagedAgent(parentPiboSessionId, agentId) {
|
|
2173
2098
|
const child = this.requireManagedAgent(parentPiboSessionId, agentId);
|
|
@@ -2615,7 +2540,10 @@ export class PiboSessionRouter {
|
|
|
2615
2540
|
}
|
|
2616
2541
|
}
|
|
2617
2542
|
projectKnownSessionSignals() {
|
|
2618
|
-
|
|
2543
|
+
const sessions = this.sessionStore.list?.() ?? [];
|
|
2544
|
+
const depthBySessionId = new Map(sessions.map((session) => [session.id, this.getSubagentDepth(session.id)]));
|
|
2545
|
+
sessions.sort((left, right) => (depthBySessionId.get(left.id) ?? 0) - (depthBySessionId.get(right.id) ?? 0));
|
|
2546
|
+
for (const session of sessions) {
|
|
2619
2547
|
this.signalRegistry.project({ type: "session_created", session });
|
|
2620
2548
|
}
|
|
2621
2549
|
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { createHash, randomUUID } from "node:crypto";
|
|
2
|
+
import { outputIdentityFingerprint, outputPartFingerprint } from "../core/output-render-sequence.js";
|
|
2
3
|
import { rootSessionId } from "./session-store.js";
|
|
3
4
|
export class PiboOutputIdentityCollisionError extends Error {
|
|
4
5
|
idempotencyKey;
|
|
@@ -89,7 +90,8 @@ export class ChatDataIngestService {
|
|
|
89
90
|
ingestOutputEvent(input) {
|
|
90
91
|
const event = input.event;
|
|
91
92
|
const idempotencyKey = outputIdempotencyKey(event);
|
|
92
|
-
const identityFingerprint =
|
|
93
|
+
const identityFingerprint = outputIdentityFingerprint(event);
|
|
94
|
+
const partFingerprint = isOutputPartEvent(event) ? outputPartFingerprint(event) : undefined;
|
|
93
95
|
if (idempotencyKey) {
|
|
94
96
|
const existing = this.store.eventLog.findByIdempotencyKey(idempotencyKey);
|
|
95
97
|
if (existing) {
|
|
@@ -156,6 +158,7 @@ export class ChatDataIngestService {
|
|
|
156
158
|
previewText: previewTextForOutputEvent(event),
|
|
157
159
|
attributes: compactObject({
|
|
158
160
|
identityFingerprint,
|
|
161
|
+
outputPartFingerprint: partFingerprint,
|
|
159
162
|
eventIdentityScoped: eventIdForOutputEvent(event) !== undefined,
|
|
160
163
|
semanticEventId: eventIdForOutputEvent(event),
|
|
161
164
|
legacyStreamId: input.legacyStreamId,
|
|
@@ -270,11 +273,6 @@ function deterministicId(prefix, value) {
|
|
|
270
273
|
function hashJson(value) {
|
|
271
274
|
return createHash("sha256").update(canonicalJson(value)).digest("hex").slice(0, 16);
|
|
272
275
|
}
|
|
273
|
-
function outputEventFingerprint(event) {
|
|
274
|
-
const identity = { ...event };
|
|
275
|
-
delete identity.renderSequence;
|
|
276
|
-
return createHash("sha256").update(canonicalJson(identity)).digest("hex");
|
|
277
|
-
}
|
|
278
276
|
function canonicalJson(value) {
|
|
279
277
|
if (value === null || typeof value !== "object")
|
|
280
278
|
return JSON.stringify(value) ?? "null";
|
|
@@ -470,6 +468,16 @@ function observationStatusForOutputEvent(event) {
|
|
|
470
468
|
return "error";
|
|
471
469
|
return "completed";
|
|
472
470
|
}
|
|
471
|
+
function isOutputPartEvent(event) {
|
|
472
|
+
return event.type === "assistant_delta"
|
|
473
|
+
|| event.type === "assistant_message"
|
|
474
|
+
|| event.type === "thinking_started"
|
|
475
|
+
|| event.type === "thinking_delta"
|
|
476
|
+
|| event.type === "thinking_finished"
|
|
477
|
+
|| event.type === "assistant_usage"
|
|
478
|
+
|| event.type === "compaction_start"
|
|
479
|
+
|| event.type === "compaction_end";
|
|
480
|
+
}
|
|
473
481
|
function isTerminalOutputEvent(event) {
|
|
474
482
|
return !event.type.endsWith("_started") && event.type !== "tool_call" && event.type !== "assistant_delta" && event.type !== "thinking_delta" && event.type !== "tool_execution_updated";
|
|
475
483
|
}
|
package/dist/data/pibo-store.js
CHANGED
|
@@ -8,7 +8,7 @@ import { MessageStore } from "./message-store.js";
|
|
|
8
8
|
import { NavigationStore } from "./navigation-store.js";
|
|
9
9
|
import { ObservationStore } from "./observation-store.js";
|
|
10
10
|
import { PayloadStore } from "./payload-store.js";
|
|
11
|
-
import { applyPiboDataSchema } from "./schema.js";
|
|
11
|
+
import { applyPiboDataSchema, assertSupportedPiboDataSchemaVersion } from "./schema.js";
|
|
12
12
|
import { TelemetryStore } from "./telemetry.js";
|
|
13
13
|
import { SessionStore } from "./session-store.js";
|
|
14
14
|
export class PiboDataStore {
|
|
@@ -27,6 +27,13 @@ export class PiboDataStore {
|
|
|
27
27
|
if (this.path !== ":memory:")
|
|
28
28
|
mkdirSync(dirname(this.path), { recursive: true });
|
|
29
29
|
this.db = new DatabaseSync(this.path);
|
|
30
|
+
try {
|
|
31
|
+
assertSupportedPiboDataSchemaVersion(this.db);
|
|
32
|
+
}
|
|
33
|
+
catch (error) {
|
|
34
|
+
this.db.close();
|
|
35
|
+
throw error;
|
|
36
|
+
}
|
|
30
37
|
if (insidePiboHome)
|
|
31
38
|
protectPrivateFileSync(this.path);
|
|
32
39
|
this.db.exec("PRAGMA busy_timeout = 5000");
|
package/dist/data/schema.js
CHANGED
|
@@ -1,4 +1,104 @@
|
|
|
1
1
|
export const PIBO_DATA_SCHEMA_VERSION = 7;
|
|
2
|
+
const retiredScopeColumn = ["owner", "scope"].join("_");
|
|
3
|
+
const retiredScopeTables = [
|
|
4
|
+
{
|
|
5
|
+
name: "sessions",
|
|
6
|
+
columns: [
|
|
7
|
+
"id", "pi_session_id", "room_id", "root_session_id", "parent_id", "origin_id",
|
|
8
|
+
"channel", "kind", "profile", "active_model_json", "workspace", "title",
|
|
9
|
+
"first_message_preview", "status", "archived_at", "deleted_at", "metadata_json",
|
|
10
|
+
"created_at", "updated_at", "last_activity_at",
|
|
11
|
+
],
|
|
12
|
+
definition: `
|
|
13
|
+
id TEXT PRIMARY KEY,
|
|
14
|
+
pi_session_id TEXT UNIQUE,
|
|
15
|
+
room_id TEXT,
|
|
16
|
+
root_session_id TEXT,
|
|
17
|
+
parent_id TEXT,
|
|
18
|
+
origin_id TEXT,
|
|
19
|
+
channel TEXT NOT NULL,
|
|
20
|
+
kind TEXT NOT NULL,
|
|
21
|
+
profile TEXT NOT NULL,
|
|
22
|
+
active_model_json TEXT,
|
|
23
|
+
workspace TEXT,
|
|
24
|
+
title TEXT NOT NULL DEFAULT 'Untitled Session',
|
|
25
|
+
first_message_preview TEXT,
|
|
26
|
+
status TEXT NOT NULL DEFAULT 'idle',
|
|
27
|
+
archived_at TEXT,
|
|
28
|
+
deleted_at TEXT,
|
|
29
|
+
metadata_json TEXT NOT NULL DEFAULT '{}',
|
|
30
|
+
created_at TEXT NOT NULL,
|
|
31
|
+
updated_at TEXT NOT NULL,
|
|
32
|
+
last_activity_at TEXT NOT NULL
|
|
33
|
+
`,
|
|
34
|
+
},
|
|
35
|
+
{
|
|
36
|
+
name: "rooms",
|
|
37
|
+
columns: [
|
|
38
|
+
"id", "name", "topic", "type", "parent_room_id", "workspace", "archived_at",
|
|
39
|
+
"retention_policy_id", "metadata_json", "created_at", "updated_at",
|
|
40
|
+
],
|
|
41
|
+
definition: `
|
|
42
|
+
id TEXT PRIMARY KEY,
|
|
43
|
+
name TEXT NOT NULL,
|
|
44
|
+
topic TEXT,
|
|
45
|
+
type TEXT NOT NULL,
|
|
46
|
+
parent_room_id TEXT,
|
|
47
|
+
workspace TEXT,
|
|
48
|
+
archived_at TEXT,
|
|
49
|
+
retention_policy_id TEXT,
|
|
50
|
+
metadata_json TEXT NOT NULL DEFAULT '{}',
|
|
51
|
+
created_at TEXT NOT NULL,
|
|
52
|
+
updated_at TEXT NOT NULL
|
|
53
|
+
`,
|
|
54
|
+
},
|
|
55
|
+
{
|
|
56
|
+
name: "session_navigation",
|
|
57
|
+
columns: [
|
|
58
|
+
"room_id", "session_id", "root_session_id", "parent_id", "origin_id", "title",
|
|
59
|
+
"profile", "status", "archived_at", "last_activity_at", "last_message_preview",
|
|
60
|
+
"child_count", "sort_key", "updated_at",
|
|
61
|
+
],
|
|
62
|
+
definition: `
|
|
63
|
+
room_id TEXT,
|
|
64
|
+
session_id TEXT PRIMARY KEY,
|
|
65
|
+
root_session_id TEXT,
|
|
66
|
+
parent_id TEXT,
|
|
67
|
+
origin_id TEXT,
|
|
68
|
+
title TEXT NOT NULL,
|
|
69
|
+
profile TEXT NOT NULL,
|
|
70
|
+
status TEXT NOT NULL,
|
|
71
|
+
archived_at TEXT,
|
|
72
|
+
last_activity_at TEXT NOT NULL,
|
|
73
|
+
last_message_preview TEXT,
|
|
74
|
+
child_count INTEGER NOT NULL DEFAULT 0,
|
|
75
|
+
sort_key TEXT NOT NULL,
|
|
76
|
+
updated_at TEXT NOT NULL
|
|
77
|
+
`,
|
|
78
|
+
},
|
|
79
|
+
];
|
|
80
|
+
function retiredScopeTablesToRebuild(db) {
|
|
81
|
+
return retiredScopeTables.filter((table) => ((db.prepare(`SELECT 1 FROM pragma_table_info('${table.name}') WHERE name = ?`).get(retiredScopeColumn)) !== undefined));
|
|
82
|
+
}
|
|
83
|
+
function rebuildRetiredScopeTables(db, tablesToRebuild) {
|
|
84
|
+
for (const table of tablesToRebuild) {
|
|
85
|
+
const replacement = `__pibo_schema_v7_${table.name}`;
|
|
86
|
+
const columnList = table.columns.join(", ");
|
|
87
|
+
db.exec(`
|
|
88
|
+
CREATE TABLE ${replacement} (${table.definition});
|
|
89
|
+
INSERT INTO ${replacement} (${columnList}) SELECT ${columnList} FROM ${table.name};
|
|
90
|
+
DROP TABLE ${table.name};
|
|
91
|
+
ALTER TABLE ${replacement} RENAME TO ${table.name};
|
|
92
|
+
`);
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
export function assertSupportedPiboDataSchemaVersion(db) {
|
|
96
|
+
const version = Number(db.prepare("PRAGMA user_version").get()?.user_version ?? 0);
|
|
97
|
+
if (version > PIBO_DATA_SCHEMA_VERSION) {
|
|
98
|
+
throw new Error(`Pibo database schema version ${version} is newer than supported version ${PIBO_DATA_SCHEMA_VERSION}`);
|
|
99
|
+
}
|
|
100
|
+
return version;
|
|
101
|
+
}
|
|
2
102
|
export const PIBO_DATA_SCHEMA_MIGRATION_STEPS = [
|
|
3
103
|
"schema",
|
|
4
104
|
"runtime-bindings",
|
|
@@ -11,11 +111,27 @@ export const PIBO_DATA_SCHEMA_MIGRATION_STEPS = [
|
|
|
11
111
|
"user-version",
|
|
12
112
|
];
|
|
13
113
|
export function applyPiboDataSchema(db, hooks = {}) {
|
|
114
|
+
const previousVersion = assertSupportedPiboDataSchemaVersion(db);
|
|
115
|
+
const tablesToRebuild = retiredScopeTablesToRebuild(db);
|
|
14
116
|
const ownsTransaction = !db.isTransaction;
|
|
15
|
-
if (ownsTransaction)
|
|
16
|
-
|
|
117
|
+
if (!ownsTransaction && tablesToRebuild.length > 0) {
|
|
118
|
+
throw new Error("Pibo data schema migration requires an independent transaction");
|
|
119
|
+
}
|
|
120
|
+
const foreignKeysEnabled = ownsTransaction
|
|
121
|
+
&& tablesToRebuild.length > 0
|
|
122
|
+
&& Number(db.prepare("PRAGMA foreign_keys").get().foreign_keys ?? 0) === 1;
|
|
123
|
+
if (foreignKeysEnabled)
|
|
124
|
+
db.exec("PRAGMA foreign_keys = OFF");
|
|
17
125
|
try {
|
|
18
|
-
|
|
126
|
+
if (ownsTransaction)
|
|
127
|
+
db.exec("BEGIN IMMEDIATE");
|
|
128
|
+
applyPiboDataSchemaInTransaction(db, hooks, previousVersion, tablesToRebuild);
|
|
129
|
+
if (tablesToRebuild.length > 0) {
|
|
130
|
+
const violations = db.prepare("PRAGMA foreign_key_check").all();
|
|
131
|
+
if (violations.length > 0) {
|
|
132
|
+
throw new Error(`Pibo data schema migration would retain ${violations.length} foreign-key violation(s)`);
|
|
133
|
+
}
|
|
134
|
+
}
|
|
19
135
|
if (ownsTransaction)
|
|
20
136
|
db.exec("COMMIT");
|
|
21
137
|
}
|
|
@@ -24,12 +140,16 @@ export function applyPiboDataSchema(db, hooks = {}) {
|
|
|
24
140
|
db.exec("ROLLBACK");
|
|
25
141
|
throw error;
|
|
26
142
|
}
|
|
143
|
+
finally {
|
|
144
|
+
if (foreignKeysEnabled)
|
|
145
|
+
db.exec("PRAGMA foreign_keys = ON");
|
|
146
|
+
}
|
|
27
147
|
}
|
|
28
|
-
function applyPiboDataSchemaInTransaction(db, hooks) {
|
|
29
|
-
const previousVersion = Number(db.prepare("PRAGMA user_version").get()?.user_version ?? 0);
|
|
148
|
+
function applyPiboDataSchemaInTransaction(db, hooks, previousVersion, tablesToRebuild) {
|
|
30
149
|
const existingSessionCount = db.prepare("SELECT COUNT(*) AS count FROM sqlite_schema WHERE type = 'table' AND name = 'sessions'").get();
|
|
31
150
|
const hadSessionsBeforeMigration = existingSessionCount.count > 0
|
|
32
151
|
&& Number(db.prepare("SELECT COUNT(*) AS count FROM sessions").get().count) > 0;
|
|
152
|
+
rebuildRetiredScopeTables(db, tablesToRebuild);
|
|
33
153
|
db.exec(`
|
|
34
154
|
CREATE TABLE IF NOT EXISTS sessions (
|
|
35
155
|
id TEXT PRIMARY KEY,
|
|
@@ -79,6 +199,16 @@ function applyPiboDataSchemaInTransaction(db, hooks) {
|
|
|
79
199
|
FOREIGN KEY (pibo_session_id) REFERENCES sessions(id) ON DELETE CASCADE
|
|
80
200
|
);
|
|
81
201
|
|
|
202
|
+
CREATE TABLE IF NOT EXISTS session_output_part_counters (
|
|
203
|
+
pibo_session_id TEXT NOT NULL,
|
|
204
|
+
event_id TEXT NOT NULL,
|
|
205
|
+
part_kind TEXT NOT NULL CHECK(part_kind IN ('assistant', 'thinking', 'usage', 'compaction')),
|
|
206
|
+
next_index INTEGER NOT NULL CHECK(next_index >= 0),
|
|
207
|
+
updated_at TEXT NOT NULL,
|
|
208
|
+
PRIMARY KEY (pibo_session_id, event_id, part_kind),
|
|
209
|
+
FOREIGN KEY (pibo_session_id) REFERENCES sessions(id) ON DELETE CASCADE
|
|
210
|
+
);
|
|
211
|
+
|
|
82
212
|
CREATE TABLE IF NOT EXISTS session_tool_invocation_counters (
|
|
83
213
|
pibo_session_id TEXT NOT NULL,
|
|
84
214
|
event_id TEXT NOT NULL,
|