@pasko70/pibo 3.0.1 → 3.1.0
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/README.md +20 -6
- package/compute-image/Dockerfile +47 -0
- package/compute-image/Dockerfile.dockerignore +5 -0
- package/dist/agent-runtime/context-build.js +26 -2
- 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 +120 -15
- package/dist/agent-runtimes/codex-native/adapter.js +12 -0
- package/dist/agent-runtimes/codex-native/resource-delivery.js +35 -7
- package/dist/agent-runtimes/omp/adapter.js +3 -7
- package/dist/agent-runtimes/pi/adapter.js +5 -3
- package/dist/agent-runtimes/pi/routed-session.js +59 -12
- package/dist/agent-runtimes/pi/runtime.js +6 -0
- package/dist/apps/chat/agent-profiles.js +31 -2
- package/dist/apps/chat/agent-store.js +98 -6
- package/dist/apps/chat/chat-request-normalizers.js +31 -0
- package/dist/apps/chat/data/chat-data-mappers.js +61 -11
- package/dist/apps/chat/data/project-service.js +119 -15
- package/dist/apps/chat/data/room-service.js +21 -0
- package/dist/apps/chat/data/session-query-service.js +33 -10
- package/dist/apps/chat/data/timeline-query-service.js +60 -35
- package/dist/apps/chat/loop-api.js +10 -2
- package/dist/apps/chat/output-compactor.js +144 -21
- package/dist/apps/chat/output-event-policy.js +179 -2
- package/dist/apps/chat/stream.js +16 -7
- package/dist/apps/chat/trace-v2.js +1 -0
- package/dist/apps/chat/web-app.js +317 -86
- package/dist/apps/chat-ui/assets/{dist-BDIATCQt.js → dist-CUQJqggN.js} +1 -1
- package/dist/apps/chat-ui/assets/{dist-mqCviI-c.js → dist-ClUlQWYN.js} +1 -1
- package/dist/apps/chat-ui/assets/{dist-JnW4v8UE.js → dist-Djul9BmZ.js} +1 -1
- package/dist/apps/chat-ui/assets/{dist-lRiLP9rr.js → dist-GD0JGdCi.js} +1 -1
- package/dist/apps/chat-ui/assets/{dist-BBDMeU5-.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/apps/cli-ui/cliSessionsCommand.js +34 -8
- package/dist/cli-session/localSessionSource.js +153 -161
- package/dist/cli.js +7 -11
- package/dist/compute/cli.js +35 -16
- package/dist/compute/docker.js +341 -94
- package/dist/config/config.js +3 -0
- package/dist/core/context-build.js +7 -16
- package/dist/core/output-persistence-retry.js +484 -0
- package/dist/core/output-render-sequence.js +331 -0
- package/dist/core/profiles.js +24 -0
- package/dist/core/provider-recovery.js +7 -1
- package/dist/core/session-model.js +37 -1
- package/dist/core/session-router.js +78 -110
- package/dist/data/ingest-service.js +91 -9
- package/dist/data/navigation-store.js +5 -5
- package/dist/data/pibo-store.js +8 -1
- package/dist/data/schema.js +243 -3
- package/dist/data/session-store.js +3 -3
- package/dist/debug/agents.js +91 -89
- package/dist/debug/index.js +46 -2
- package/dist/debug/pty.js +127 -48
- package/dist/gateway/cli.js +4 -0
- package/dist/gateway/web.js +8 -8
- package/dist/local/client.js +3 -2
- 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 +36 -6
- package/dist/plugins/context-files.js +52 -1
- package/dist/plugins/registry.js +2 -0
- package/dist/plugins/user-profile-resources.js +22 -0
- 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 +378 -106
- package/dist/session-ui/terminalRows.js +8 -4
- package/dist/sessions/pibo-data-store.js +145 -0
- package/dist/sessions/store.js +42 -0
- package/dist/setup/cli.js +422 -6
- package/dist/setup/installation-profiles.js +464 -0
- package/dist/shared/deterministic-digest.js +94 -0
- package/dist/shared/trace-engine.js +54 -0
- package/dist/shared/trace-event-projection.js +124 -67
- package/dist/shared/trace-history.js +47 -12
- package/dist/shared/trace-live-reducer.js +23 -4
- package/dist/shared/trace-nodes.js +17 -28
- package/dist/shared/trace-order.js +41 -5
- package/dist/shared/trace-page-merge.js +44 -14
- package/dist/shared/trace-patch-nodes.js +2 -0
- package/dist/shared/trace-subagent-links.js +4 -1
- package/dist/shared/trace-tool-identity.js +15 -8
- package/dist/signals/projector.js +28 -6
- package/dist/subagents/observation-query.js +121 -0
- package/dist/subagents/tool.js +7 -3
- 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/docs/ops/vscode-extension-release.md +9 -1
- package/npm-shrinkwrap.json +9 -2
- package/package.json +9 -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-0XOOVxLP.js +0 -228
- package/dist/apps/chat-ui/assets/index-DV7_CgsC.css +0 -1
- package/dist/apps/chat-vscode-web/assets/index-Dtw2Z7jz.js +0 -43
- package/dist/apps/vscode-artifacts/latest.vsix +0 -0
- package/dist/apps/vscode-artifacts/pibo-vscode-ext-3.0.1.vsix +0 -0
|
@@ -2,10 +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
|
+
import { OutputRenderSequencer, outputRenderHighWaterStore } from "./output-render-sequence.js";
|
|
7
8
|
import { normalizePiboAgentSessionName, } from "../subagents/tool.js";
|
|
8
|
-
import {
|
|
9
|
+
import { piboAgentObservationDetails, piboAgentObservationKind, piboAgentObservationRole, piboAgentObservationSourceFromEvent, piboAgentObservationText, } from "../subagents/observations.js";
|
|
10
|
+
import { preparePiboAgentObservationQuery, selectPiboAgentObservationPage, } from "../subagents/observation-query.js";
|
|
9
11
|
import { PiboRunRegistry } from "../runs/registry.js";
|
|
10
12
|
import { PiboRunCancellationError, PiboRunCancelledError, PiboRunExecutionTimeoutError, waitForRunCancellationSettlement } from "../runs/lifecycle.js";
|
|
11
13
|
import { PiboRunResourceLimitError } from "../runs/resource-isolation.js";
|
|
@@ -20,7 +22,7 @@ import { getDefaultPiboWorkspace } from "./workspace.js";
|
|
|
20
22
|
import { loadPiboModelDefaults, selectRequestedFastMode } from "./model-defaults.js";
|
|
21
23
|
import { loadPiboGatewaySettings } from "./gateway-settings.js";
|
|
22
24
|
import { loadPiboUserSettings } from "./user-settings.js";
|
|
23
|
-
import { resolvePiboSessionActiveModel } from "./session-model.js";
|
|
25
|
+
import { PIBO_INITIAL_MODEL_FALLBACKS_METADATA_KEY, resolvePiboSessionActiveModel, resolvePiboSessionModelFallbacks, withPiboSessionModelFallbacksMetadata, } from "./session-model.js";
|
|
24
26
|
import { isPiboThinkingLevel } from "./thinking.js";
|
|
25
27
|
import { RuntimeSessionRegistry } from "../tools/runtime/registry.js";
|
|
26
28
|
import { GatewayWorkAdmissionController } from "./gateway-resource-guard.js";
|
|
@@ -93,6 +95,7 @@ function profileForSession(baseProfile, runtimeInstanceId, nativeSessionId, pare
|
|
|
93
95
|
parentSessionId: parentNativeSessionId,
|
|
94
96
|
model: usesProfileRuntime ? baseProfile.model : undefined,
|
|
95
97
|
mainModel: usesProfileRuntime ? baseProfile.mainModel : undefined,
|
|
98
|
+
mainModelFallbacks: usesProfileRuntime ? baseProfile.mainModelFallbacks : undefined,
|
|
96
99
|
subagentModel: usesProfileRuntime ? baseProfile.subagentModel : undefined,
|
|
97
100
|
thinkingLevel: baseProfile.thinkingLevel,
|
|
98
101
|
mainThinkingLevel: baseProfile.mainThinkingLevel,
|
|
@@ -157,7 +160,11 @@ function formatRunReminderMessage(notification) {
|
|
|
157
160
|
toolName: run.toolName,
|
|
158
161
|
summary: run.summary,
|
|
159
162
|
})),
|
|
160
|
-
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(" "),
|
|
161
168
|
}),
|
|
162
169
|
"</pibo_run_notification>",
|
|
163
170
|
].join("\n");
|
|
@@ -329,6 +336,7 @@ export class PiboSessionRouter {
|
|
|
329
336
|
sessions = new Map();
|
|
330
337
|
pendingSessions = new Map();
|
|
331
338
|
listeners = new Set();
|
|
339
|
+
outputRenderSequencer;
|
|
332
340
|
runRegistry;
|
|
333
341
|
gatewayWorkAdmission = new GatewayWorkAdmissionController();
|
|
334
342
|
signalRegistry;
|
|
@@ -374,6 +382,9 @@ export class PiboSessionRouter {
|
|
|
374
382
|
// Preserve that composition contract during the adapter migration without branching on adapter ids.
|
|
375
383
|
this.compatibilityRuntimeRegistry = options.pluginRegistry ? createDefaultPiboPluginRegistry() : undefined;
|
|
376
384
|
this.sessionStore = options.sessionStore ?? new InMemoryPiboSessionStore();
|
|
385
|
+
this.outputRenderSequencer = new OutputRenderSequencer({
|
|
386
|
+
highWaterStore: outputRenderHighWaterStore(this.sessionStore),
|
|
387
|
+
});
|
|
377
388
|
this.telemetryStore = options.telemetryStore ?? telemetryStoreFromSessionStore(this.sessionStore);
|
|
378
389
|
this.telemetryWriter = this.telemetryStore ? new AsyncTelemetryWriter(this.telemetryStore) : undefined;
|
|
379
390
|
this.telemetryRecorder = this.telemetryStore
|
|
@@ -419,9 +430,15 @@ export class PiboSessionRouter {
|
|
|
419
430
|
this.signalRegistry.project({ type: "session_created", session });
|
|
420
431
|
this.signalRegistry.project({ type: "pibo_output", event: recovery.event });
|
|
421
432
|
}
|
|
433
|
+
const recoveredRunReminderControllers = new Set();
|
|
422
434
|
for (const run of this.runRegistry.listAll({ includeConsumed: true, includeDetached: true })) {
|
|
423
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
|
+
}
|
|
424
439
|
}
|
|
440
|
+
for (const piboSessionId of recoveredRunReminderControllers)
|
|
441
|
+
this.scheduleRunReminder(piboSessionId, false);
|
|
425
442
|
}
|
|
426
443
|
subscribe(listener) {
|
|
427
444
|
this.listeners.add(listener);
|
|
@@ -700,6 +717,7 @@ export class PiboSessionRouter {
|
|
|
700
717
|
if (this.disposingSessions.get(id) === operation)
|
|
701
718
|
this.disposingSessions.delete(id);
|
|
702
719
|
this.quiescingSessions.delete(id);
|
|
720
|
+
this.outputRenderSequencer.disposeSession(id);
|
|
703
721
|
this.signalRegistry.project({ type: "session_disposed", piboSessionId: id, reason });
|
|
704
722
|
}
|
|
705
723
|
await this.telemetryWriter?.flush();
|
|
@@ -916,8 +934,12 @@ export class PiboSessionRouter {
|
|
|
916
934
|
expectedRevision: input.expectedRevision,
|
|
917
935
|
mode,
|
|
918
936
|
});
|
|
919
|
-
if (switchingRuntime)
|
|
920
|
-
this.sessionStore.update(piboSessionId, {
|
|
937
|
+
if (switchingRuntime) {
|
|
938
|
+
this.sessionStore.update(piboSessionId, {
|
|
939
|
+
activeModel: null,
|
|
940
|
+
metadata: withPiboSessionModelFallbacksMetadata(session.metadata, []),
|
|
941
|
+
});
|
|
942
|
+
}
|
|
921
943
|
return structuredClone(persisted);
|
|
922
944
|
}
|
|
923
945
|
getSessionRuntimeStatus(piboSessionId) {
|
|
@@ -1123,6 +1145,7 @@ export class PiboSessionRouter {
|
|
|
1123
1145
|
this.runtimeResourceSessions.clear();
|
|
1124
1146
|
this.activeSubagentRequests.clear();
|
|
1125
1147
|
this.subagentRequestIdsByEvent.clear();
|
|
1148
|
+
this.outputRenderSequencer.disposeAll();
|
|
1126
1149
|
this.agentObservations.length = 0;
|
|
1127
1150
|
this.agentObservationEvictedThroughByParent.clear();
|
|
1128
1151
|
await this.telemetryWriter?.dispose();
|
|
@@ -1229,6 +1252,9 @@ export class PiboSessionRouter {
|
|
|
1229
1252
|
const activeModel = changesRuntimeModelNamespace
|
|
1230
1253
|
? undefined
|
|
1231
1254
|
: this.ensureSessionActiveModel(piboSession, sessionProfile, parentModelScopeId, modelDefaults);
|
|
1255
|
+
const modelFallbacks = changesRuntimeModelNamespace
|
|
1256
|
+
? []
|
|
1257
|
+
: this.ensureSessionModelFallbacks(piboSession, sessionProfile, parentModelScopeId, activeModel);
|
|
1232
1258
|
const userSettings = loadPiboUserSettings();
|
|
1233
1259
|
const telemetryExtension = this.telemetryStore
|
|
1234
1260
|
? createPiboProviderTelemetryExtension({ store: this.telemetryStore, writer: this.telemetryWriter, session: piboSession, model: activeModel })
|
|
@@ -1373,6 +1399,7 @@ export class PiboSessionRouter {
|
|
|
1373
1399
|
],
|
|
1374
1400
|
modelDefaults,
|
|
1375
1401
|
initialFastMode,
|
|
1402
|
+
providerFallbacksEnabled: modelFallbacks.length > 0,
|
|
1376
1403
|
},
|
|
1377
1404
|
},
|
|
1378
1405
|
});
|
|
@@ -1472,6 +1499,7 @@ export class PiboSessionRouter {
|
|
|
1472
1499
|
this.handleInterruptedRunReminders(messages);
|
|
1473
1500
|
},
|
|
1474
1501
|
messagePreflight: this.options.messagePreflight,
|
|
1502
|
+
modelFallbacks,
|
|
1475
1503
|
getRuntimeAuthStatus: () => runtimeRegistry.getAgentRuntimeAuthStatus(binding.runtimeInstanceId),
|
|
1476
1504
|
startRuntimeAuth: async (input) => {
|
|
1477
1505
|
const { runtimeInstanceId: _runtimeInstanceId, ...result } = await runtimeRegistry.startAgentRuntimeAuth(binding.runtimeInstanceId, input);
|
|
@@ -1612,6 +1640,20 @@ export class PiboSessionRouter {
|
|
|
1612
1640
|
}
|
|
1613
1641
|
return activeModel;
|
|
1614
1642
|
}
|
|
1643
|
+
ensureSessionModelFallbacks(piboSession, profile, parentPiSessionId, activeModel) {
|
|
1644
|
+
const modelFallbacks = resolvePiboSessionModelFallbacks({
|
|
1645
|
+
profile,
|
|
1646
|
+
piboSession,
|
|
1647
|
+
parentPiSessionId,
|
|
1648
|
+
activeModel,
|
|
1649
|
+
});
|
|
1650
|
+
if (piboSession.metadata?.[PIBO_INITIAL_MODEL_FALLBACKS_METADATA_KEY] === undefined) {
|
|
1651
|
+
this.sessionStore.update(piboSession.id, {
|
|
1652
|
+
metadata: withPiboSessionModelFallbacksMetadata(piboSession.metadata, modelFallbacks),
|
|
1653
|
+
});
|
|
1654
|
+
}
|
|
1655
|
+
return modelFallbacks;
|
|
1656
|
+
}
|
|
1615
1657
|
resolveModelDefaults() {
|
|
1616
1658
|
if (typeof this.options.modelDefaults === "function")
|
|
1617
1659
|
return this.options.modelDefaults();
|
|
@@ -2042,98 +2084,15 @@ export class PiboSessionRouter {
|
|
|
2042
2084
|
return session;
|
|
2043
2085
|
}
|
|
2044
2086
|
observeManagedAgents(parentPiboSessionId, input) {
|
|
2045
|
-
const
|
|
2046
|
-
|
|
2047
|
-
const
|
|
2048
|
-
const
|
|
2049
|
-
|
|
2050
|
-
|
|
2051
|
-
|
|
2052
|
-
|
|
2053
|
-
|
|
2054
|
-
const requestIds = input.requestIds ? new Set(input.requestIds) : undefined;
|
|
2055
|
-
const agentIds = input.agentIds ? new Set(input.agentIds) : undefined;
|
|
2056
|
-
if (agentIds) {
|
|
2057
|
-
for (const agentId of agentIds)
|
|
2058
|
-
this.requireManagedAgent(parentPiboSessionId, agentId);
|
|
2059
|
-
}
|
|
2060
|
-
const names = input.names ? new Set(input.names) : undefined;
|
|
2061
|
-
const threadKeys = input.threadKeys ? new Set(input.threadKeys) : undefined;
|
|
2062
|
-
const eventTypes = input.eventTypes ? new Set(input.eventTypes) : undefined;
|
|
2063
|
-
const kinds = input.kinds ? new Set(input.kinds) : undefined;
|
|
2064
|
-
const roles = input.roles ? new Set(input.roles) : undefined;
|
|
2065
|
-
const textContains = input.textContains?.toLowerCase();
|
|
2066
|
-
const defaultMessageView = eventTypes === undefined && kinds === undefined;
|
|
2067
|
-
const explicitlySelectsTools = input.eventTypes?.some((eventType) => piboAgentObservationKind(eventType) === "tool") === true
|
|
2068
|
-
|| input.kinds?.includes("tool") === true;
|
|
2069
|
-
const includeTools = input.includeTools === true || (input.includeTools === undefined && explicitlySelectsTools);
|
|
2070
|
-
const defaultEventTypes = includeTools
|
|
2071
|
-
? [...PIBO_AGENT_OBSERVATION_DEFAULT_EVENT_TYPES, ...PIBO_AGENT_OBSERVATION_DEFAULT_TOOL_EVENT_TYPES]
|
|
2072
|
-
: [...PIBO_AGENT_OBSERVATION_DEFAULT_EVENT_TYPES];
|
|
2073
|
-
const defaultEventTypeSet = new Set(defaultEventTypes);
|
|
2074
|
-
const matches = this.agentObservations.filter((observation) => {
|
|
2075
|
-
if (observation.managingParentId !== parentPiboSessionId)
|
|
2076
|
-
return false;
|
|
2077
|
-
if (requestIds && (!observation.requestId || !requestIds.has(observation.requestId)))
|
|
2078
|
-
return false;
|
|
2079
|
-
if (agentIds && !agentIds.has(observation.agentId))
|
|
2080
|
-
return false;
|
|
2081
|
-
if (names && !names.has(observation.name))
|
|
2082
|
-
return false;
|
|
2083
|
-
if (threadKeys && (!observation.threadKey || !threadKeys.has(observation.threadKey)))
|
|
2084
|
-
return false;
|
|
2085
|
-
if (observation.kind === "tool" && !includeTools)
|
|
2086
|
-
return false;
|
|
2087
|
-
if (defaultMessageView && !defaultEventTypeSet.has(observation.eventType))
|
|
2088
|
-
return false;
|
|
2089
|
-
if (eventTypes && !eventTypes.has(observation.eventType))
|
|
2090
|
-
return false;
|
|
2091
|
-
if (kinds && !kinds.has(observation.kind))
|
|
2092
|
-
return false;
|
|
2093
|
-
if (roles && (!observation.role || !roles.has(observation.role)))
|
|
2094
|
-
return false;
|
|
2095
|
-
if (afterSequence !== undefined && observation.sequence <= afterSequence)
|
|
2096
|
-
return false;
|
|
2097
|
-
const createdAt = Date.parse(observation.createdAt);
|
|
2098
|
-
if (since !== undefined && createdAt < since)
|
|
2099
|
-
return false;
|
|
2100
|
-
if (until !== undefined && createdAt > until)
|
|
2101
|
-
return false;
|
|
2102
|
-
if (textContains && !(observation.text ?? "").toLowerCase().includes(textContains))
|
|
2103
|
-
return false;
|
|
2104
|
-
return true;
|
|
2105
|
-
});
|
|
2106
|
-
const cursorPolling = afterSequence !== undefined;
|
|
2107
|
-
const ordered = cursorPolling
|
|
2108
|
-
? matches.slice(0, limit)
|
|
2109
|
-
: [...matches].sort((left, right) => order === "asc" ? left.sequence - right.sequence : right.sequence - left.sequence).slice(0, limit);
|
|
2110
|
-
if (cursorPolling && order === "desc")
|
|
2111
|
-
ordered.reverse();
|
|
2112
|
-
const selected = ordered.map((observation) => {
|
|
2113
|
-
const { managingParentId: _managingParentId, details, ...visible } = observation;
|
|
2114
|
-
const compact = visible.kind === "tool" && toolDetail === "summary"
|
|
2115
|
-
? { ...visible, text: piboAgentObservationToolSummary(visible.text, visible.isError, details) }
|
|
2116
|
-
: visible;
|
|
2117
|
-
return input.includeDetails === true && details !== undefined ? { ...compact, details } : compact;
|
|
2118
|
-
});
|
|
2119
|
-
const evictedThrough = this.agentObservationEvictedThroughByParent.get(parentPiboSessionId) ?? 0;
|
|
2120
|
-
const retentionTruncated = afterSequence === undefined ? evictedThrough > 0 : afterSequence < evictedThrough;
|
|
2121
|
-
const nextAfterSequence = selected.reduce((maximum, observation) => Math.max(maximum, observation.sequence), afterSequence === undefined ? 0 : Math.max(afterSequence, evictedThrough));
|
|
2122
|
-
return {
|
|
2123
|
-
filters: {
|
|
2124
|
-
...input,
|
|
2125
|
-
...(defaultMessageView ? { eventTypes: defaultEventTypes } : {}),
|
|
2126
|
-
...(afterSequence !== undefined ? { afterSequence } : {}),
|
|
2127
|
-
order,
|
|
2128
|
-
limit,
|
|
2129
|
-
includeTools,
|
|
2130
|
-
toolDetail,
|
|
2131
|
-
includeDetails: input.includeDetails === true,
|
|
2132
|
-
},
|
|
2133
|
-
observations: selected,
|
|
2134
|
-
nextAfterSequence,
|
|
2135
|
-
truncated: retentionTruncated || matches.length > selected.length,
|
|
2136
|
-
};
|
|
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 });
|
|
2137
2096
|
}
|
|
2138
2097
|
async killManagedAgent(parentPiboSessionId, agentId) {
|
|
2139
2098
|
const child = this.requireManagedAgent(parentPiboSessionId, agentId);
|
|
@@ -2346,13 +2305,13 @@ export class PiboSessionRouter {
|
|
|
2346
2305
|
const parentChatRoomId = typeof parent.metadata?.chatRoomId === "string" ? parent.metadata.chatRoomId : undefined;
|
|
2347
2306
|
if (parentChatRoomId)
|
|
2348
2307
|
metadata.chatRoomId = parentChatRoomId;
|
|
2349
|
-
const newSessionMetadata = {
|
|
2308
|
+
const newSessionMetadata = withPiboSessionModelFallbacksMetadata({
|
|
2350
2309
|
...metadata,
|
|
2351
2310
|
...(subagent.thinkingLevel ? { initialThinkingLevel: subagent.thinkingLevel } : {}),
|
|
2352
2311
|
...(subagent.runtimeOptions && Object.keys(subagent.runtimeOptions).length > 0
|
|
2353
2312
|
? { initialRuntimeOptions: structuredClone(subagent.runtimeOptions) }
|
|
2354
2313
|
: {}),
|
|
2355
|
-
};
|
|
2314
|
+
}, subagent.modelFallbacks ?? []);
|
|
2356
2315
|
const existing = this.sessionStore.find({
|
|
2357
2316
|
channel: "pibo.subagents",
|
|
2358
2317
|
kind: "subagent",
|
|
@@ -2435,17 +2394,23 @@ export class PiboSessionRouter {
|
|
|
2435
2394
|
}
|
|
2436
2395
|
}
|
|
2437
2396
|
emitOutput = (event) => {
|
|
2438
|
-
const
|
|
2439
|
-
this.
|
|
2440
|
-
this.
|
|
2441
|
-
this.
|
|
2442
|
-
this.
|
|
2397
|
+
const positionedEvent = this.outputRenderSequencer.position(event);
|
|
2398
|
+
const session = this.sessionStore.get(positionedEvent.piboSessionId);
|
|
2399
|
+
this.recordAgentObservation(positionedEvent, session);
|
|
2400
|
+
this.telemetryRecorder?.recordOutput(positionedEvent, { session, status: this.sessions.get(positionedEvent.piboSessionId)?.getStatus() });
|
|
2401
|
+
this.signalRegistry.project({ type: "pibo_output", event: positionedEvent, session });
|
|
2402
|
+
this.pluginRegistry.notifyEvent(positionedEvent);
|
|
2443
2403
|
for (const listener of this.listeners) {
|
|
2444
|
-
|
|
2404
|
+
try {
|
|
2405
|
+
listener(positionedEvent);
|
|
2406
|
+
}
|
|
2407
|
+
catch (error) {
|
|
2408
|
+
console.error("[pibo] output listener failed", error);
|
|
2409
|
+
}
|
|
2445
2410
|
}
|
|
2446
|
-
this.handleRunReminderOutput(
|
|
2447
|
-
if (
|
|
2448
|
-
this.scheduleRunReminder(
|
|
2411
|
+
this.handleRunReminderOutput(positionedEvent);
|
|
2412
|
+
if (positionedEvent.type === "message_finished" && positionedEvent.source !== "service") {
|
|
2413
|
+
this.scheduleRunReminder(positionedEvent.piboSessionId, true);
|
|
2449
2414
|
}
|
|
2450
2415
|
};
|
|
2451
2416
|
handleRunReminderOutput(event) {
|
|
@@ -2575,7 +2540,10 @@ export class PiboSessionRouter {
|
|
|
2575
2540
|
}
|
|
2576
2541
|
}
|
|
2577
2542
|
projectKnownSessionSignals() {
|
|
2578
|
-
|
|
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) {
|
|
2579
2547
|
this.signalRegistry.project({ type: "session_created", session });
|
|
2580
2548
|
}
|
|
2581
2549
|
}
|
|
@@ -1,5 +1,18 @@
|
|
|
1
1
|
import { createHash, randomUUID } from "node:crypto";
|
|
2
2
|
import { rootSessionId } from "./session-store.js";
|
|
3
|
+
export class PiboOutputIdentityCollisionError extends Error {
|
|
4
|
+
idempotencyKey;
|
|
5
|
+
existingFingerprint;
|
|
6
|
+
incomingFingerprint;
|
|
7
|
+
code = "pibo_output_identity_collision";
|
|
8
|
+
constructor(idempotencyKey, existingFingerprint, incomingFingerprint) {
|
|
9
|
+
super(`Pibo output identity collision for "${idempotencyKey}"`);
|
|
10
|
+
this.idempotencyKey = idempotencyKey;
|
|
11
|
+
this.existingFingerprint = existingFingerprint;
|
|
12
|
+
this.incomingFingerprint = incomingFingerprint;
|
|
13
|
+
this.name = "PiboOutputIdentityCollisionError";
|
|
14
|
+
}
|
|
15
|
+
}
|
|
3
16
|
const INLINE_MESSAGE_PAYLOAD_THRESHOLD_BYTES = 16 * 1024;
|
|
4
17
|
const INLINE_JSON_PAYLOAD_THRESHOLD_BYTES = 16 * 1024;
|
|
5
18
|
export class ChatDataIngestService {
|
|
@@ -76,9 +89,39 @@ export class ChatDataIngestService {
|
|
|
76
89
|
ingestOutputEvent(input) {
|
|
77
90
|
const event = input.event;
|
|
78
91
|
const idempotencyKey = outputIdempotencyKey(event);
|
|
92
|
+
const identityFingerprint = outputEventFingerprint(event);
|
|
79
93
|
if (idempotencyKey) {
|
|
80
94
|
const existing = this.store.eventLog.findByIdempotencyKey(idempotencyKey);
|
|
81
95
|
if (existing) {
|
|
96
|
+
const existingFingerprint = typeof existing.attributes.identityFingerprint === "string"
|
|
97
|
+
? existing.attributes.identityFingerprint
|
|
98
|
+
: undefined;
|
|
99
|
+
if (existingFingerprint && existingFingerprint !== identityFingerprint) {
|
|
100
|
+
const now = input.createdAt ?? new Date().toISOString();
|
|
101
|
+
this.store.eventLog.appendEvent({
|
|
102
|
+
sessionId: input.session.id,
|
|
103
|
+
sessionSequence: this.nextEventSequence(input.session.id),
|
|
104
|
+
roomId: input.roomId,
|
|
105
|
+
topic: "pibo.diagnostic",
|
|
106
|
+
type: "pibo.output.identity_collision",
|
|
107
|
+
source: "pibo-ingest",
|
|
108
|
+
actorType: "system",
|
|
109
|
+
actorId: input.actorId,
|
|
110
|
+
eventId: eventIdForOutputEvent(event),
|
|
111
|
+
idempotencyKey: `${idempotencyKey}:collision:${identityFingerprint}`,
|
|
112
|
+
retentionClass: "audit_event",
|
|
113
|
+
previewText: `Output identity collision for ${event.type}`,
|
|
114
|
+
attributes: {
|
|
115
|
+
outputIdempotencyKey: idempotencyKey,
|
|
116
|
+
existingFingerprint,
|
|
117
|
+
incomingFingerprint: identityFingerprint,
|
|
118
|
+
incomingType: event.type,
|
|
119
|
+
},
|
|
120
|
+
createdAt: now,
|
|
121
|
+
indexedAt: now,
|
|
122
|
+
});
|
|
123
|
+
throw new PiboOutputIdentityCollisionError(idempotencyKey, existingFingerprint, identityFingerprint);
|
|
124
|
+
}
|
|
82
125
|
return {
|
|
83
126
|
streamId: existing.streamId,
|
|
84
127
|
duplicate: true,
|
|
@@ -112,6 +155,9 @@ export class ChatDataIngestService {
|
|
|
112
155
|
payloadRef,
|
|
113
156
|
previewText: previewTextForOutputEvent(event),
|
|
114
157
|
attributes: compactObject({
|
|
158
|
+
identityFingerprint,
|
|
159
|
+
eventIdentityScoped: eventIdForOutputEvent(event) !== undefined,
|
|
160
|
+
semanticEventId: eventIdForOutputEvent(event),
|
|
115
161
|
legacyStreamId: input.legacyStreamId,
|
|
116
162
|
inlinePayload: payloadRef ? undefined : toPiboJsonValue(payload?.value),
|
|
117
163
|
...attributesForOutputEvent(event),
|
|
@@ -222,7 +268,24 @@ function deterministicId(prefix, value) {
|
|
|
222
268
|
return `${prefix}_${createHash("sha256").update(value).digest("hex").slice(0, 32)}`;
|
|
223
269
|
}
|
|
224
270
|
function hashJson(value) {
|
|
225
|
-
return createHash("sha256").update(
|
|
271
|
+
return createHash("sha256").update(canonicalJson(value)).digest("hex").slice(0, 16);
|
|
272
|
+
}
|
|
273
|
+
function outputEventFingerprint(event) {
|
|
274
|
+
const identity = { ...event };
|
|
275
|
+
delete identity.renderSequence;
|
|
276
|
+
return createHash("sha256").update(canonicalJson(identity)).digest("hex");
|
|
277
|
+
}
|
|
278
|
+
function canonicalJson(value) {
|
|
279
|
+
if (value === null || typeof value !== "object")
|
|
280
|
+
return JSON.stringify(value) ?? "null";
|
|
281
|
+
if (Array.isArray(value))
|
|
282
|
+
return `[${value.map((item) => canonicalJson(item)).join(",")}]`;
|
|
283
|
+
const record = value;
|
|
284
|
+
const entries = Object.keys(record)
|
|
285
|
+
.sort()
|
|
286
|
+
.filter((key) => record[key] !== undefined)
|
|
287
|
+
.map((key) => `${JSON.stringify(key)}:${canonicalJson(record[key])}`);
|
|
288
|
+
return `{${entries.join(",")}}`;
|
|
226
289
|
}
|
|
227
290
|
function previewText(text) {
|
|
228
291
|
const normalized = text.replace(/\s+/g, " ").trim();
|
|
@@ -231,23 +294,34 @@ function previewText(text) {
|
|
|
231
294
|
function compactObject(value) {
|
|
232
295
|
return Object.fromEntries(Object.entries(value).filter(([, item]) => item !== undefined));
|
|
233
296
|
}
|
|
234
|
-
function outputIdempotencyKey(event) {
|
|
297
|
+
export function outputIdempotencyKey(event) {
|
|
235
298
|
const base = eventIdForOutputEvent(event) ?? ("toolCallId" in event ? event.toolCallId : undefined);
|
|
236
|
-
if (
|
|
237
|
-
return
|
|
238
|
-
|
|
299
|
+
if (base)
|
|
300
|
+
return `pibo.output:${event.piboSessionId}:${event.type}:${base}:${outputPartKey(event)}`;
|
|
301
|
+
if (Number.isSafeInteger(event.renderSequence) && (event.renderSequence ?? 0) > 0) {
|
|
302
|
+
return `pibo.output:${event.piboSessionId}:${event.type}:render:${event.renderSequence}`;
|
|
303
|
+
}
|
|
304
|
+
return undefined;
|
|
305
|
+
}
|
|
306
|
+
export function outputPersistenceDeliveryKey(event) {
|
|
307
|
+
return outputIdempotencyKey(event)
|
|
308
|
+
?? `pibo.output:${event.piboSessionId}:${event.type}:render:${event.renderSequence ?? "unpositioned"}`;
|
|
239
309
|
}
|
|
240
310
|
function outputPartKey(event) {
|
|
241
311
|
if (event.type === "tool_call")
|
|
242
|
-
return `${event.toolCallId}:${event.argsComplete ? "complete" : "partial"}:${hashJson(event.args)}`;
|
|
312
|
+
return `${event.toolCallId}:${event.toolInvocationOrdinal ?? 0}:${event.argsComplete ? "complete" : "partial"}:${hashJson(event.args)}`;
|
|
243
313
|
if ("toolCallId" in event)
|
|
244
|
-
return event.toolCallId ?? "main"
|
|
314
|
+
return `${event.toolCallId ?? "main"}:${event.toolInvocationOrdinal ?? 0}`;
|
|
245
315
|
if (event.type === "assistant_message")
|
|
246
316
|
return String(event.assistantIndex ?? event.contentIndex ?? 0);
|
|
247
317
|
if (event.type === "assistant_delta")
|
|
248
318
|
return String(event.assistantIndex ?? event.contentIndex ?? 0);
|
|
319
|
+
if (event.type === "assistant_usage")
|
|
320
|
+
return String(event.usageIndex ?? hashJson({ inputTokens: event.inputTokens, outputTokens: event.outputTokens, cacheReadTokens: event.cacheReadTokens, cacheWriteTokens: event.cacheWriteTokens, reasoningTokens: event.reasoningTokens, totalTokens: event.totalTokens, costUsd: event.costUsd }));
|
|
249
321
|
if (event.type === "thinking_started" || event.type === "thinking_delta" || event.type === "thinking_finished")
|
|
250
322
|
return String(event.thinkingIndex ?? event.contentIndex ?? 0);
|
|
323
|
+
if (event.type === "compaction_start" || event.type === "compaction_end")
|
|
324
|
+
return String(event.compactionIndex ?? 0);
|
|
251
325
|
if (event.type === "execution_result")
|
|
252
326
|
return event.action;
|
|
253
327
|
return "main";
|
|
@@ -318,6 +392,9 @@ function previewTextForOutputEvent(event) {
|
|
|
318
392
|
return event.type;
|
|
319
393
|
}
|
|
320
394
|
function attributesForOutputEvent(event) {
|
|
395
|
+
return { renderSequence: event.renderSequence, toolInvocationOrdinal: event.toolInvocationOrdinal, ...specificAttributesForOutputEvent(event) };
|
|
396
|
+
}
|
|
397
|
+
function specificAttributesForOutputEvent(event) {
|
|
321
398
|
if (event.type === "message_queued")
|
|
322
399
|
return { inlineText: event.text, source: event.source, queuedMessages: event.queuedMessages };
|
|
323
400
|
if (event.type === "message_steered")
|
|
@@ -330,11 +407,14 @@ function attributesForOutputEvent(event) {
|
|
|
330
407
|
return { assistantIndex: event.assistantIndex, contentIndex: event.contentIndex };
|
|
331
408
|
if (event.type === "assistant_usage")
|
|
332
409
|
return {
|
|
410
|
+
usageIndex: event.usageIndex,
|
|
333
411
|
inputTokens: event.inputTokens,
|
|
334
412
|
outputTokens: event.outputTokens,
|
|
335
413
|
cacheReadTokens: event.cacheReadTokens,
|
|
336
414
|
cacheWriteTokens: event.cacheWriteTokens,
|
|
415
|
+
reasoningTokens: event.reasoningTokens,
|
|
337
416
|
totalTokens: event.totalTokens,
|
|
417
|
+
costUsd: event.costUsd,
|
|
338
418
|
};
|
|
339
419
|
if (event.type === "thinking_started" || event.type === "thinking_delta" || event.type === "thinking_finished")
|
|
340
420
|
return { thinkingIndex: event.thinkingIndex, contentIndex: event.contentIndex };
|
|
@@ -349,7 +429,7 @@ function attributesForOutputEvent(event) {
|
|
|
349
429
|
if (event.type === "session_error")
|
|
350
430
|
return { error: event.error, ...(event.errorDetails ? { errorDetails: event.errorDetails } : {}) };
|
|
351
431
|
if (event.type === "compaction_start" || event.type === "compaction_end")
|
|
352
|
-
return { reason: event.reason, aborted: "aborted" in event ? event.aborted : undefined, errorMessage: "errorMessage" in event ? event.errorMessage : undefined };
|
|
432
|
+
return { compactionIndex: event.compactionIndex, reason: event.reason, aborted: "aborted" in event ? event.aborted : undefined, errorMessage: "errorMessage" in event ? event.errorMessage : undefined };
|
|
353
433
|
return {};
|
|
354
434
|
}
|
|
355
435
|
function observationKindForOutputEvent(event) {
|
|
@@ -398,7 +478,9 @@ function outputSessionStatus(event) {
|
|
|
398
478
|
return "error";
|
|
399
479
|
if (event.type === "message_finished")
|
|
400
480
|
return "idle";
|
|
401
|
-
|
|
481
|
+
if (event.type === "message_queued" || event.type === "message_steered" || event.type === "message_started")
|
|
482
|
+
return "running";
|
|
483
|
+
return undefined;
|
|
402
484
|
}
|
|
403
485
|
function toPiboJsonValue(value) {
|
|
404
486
|
if (value === undefined)
|
|
@@ -14,19 +14,19 @@ export class NavigationStore {
|
|
|
14
14
|
"origin_id = excluded.origin_id",
|
|
15
15
|
"title = excluded.title",
|
|
16
16
|
"profile = excluded.profile",
|
|
17
|
-
"status = excluded.status",
|
|
17
|
+
...(input.status === undefined ? [] : ["status = excluded.status"]),
|
|
18
18
|
"archived_at = excluded.archived_at",
|
|
19
|
-
"last_activity_at = excluded.last_activity_at",
|
|
19
|
+
"last_activity_at = MAX(session_navigation.last_activity_at, excluded.last_activity_at)",
|
|
20
20
|
"last_message_preview = COALESCE(excluded.last_message_preview, session_navigation.last_message_preview)",
|
|
21
21
|
"child_count = excluded.child_count",
|
|
22
|
-
"sort_key = excluded.sort_key",
|
|
23
|
-
"updated_at = excluded.updated_at",
|
|
22
|
+
"sort_key = MAX(session_navigation.sort_key, excluded.sort_key)",
|
|
23
|
+
"updated_at = MAX(session_navigation.updated_at, excluded.updated_at)",
|
|
24
24
|
];
|
|
25
25
|
this.db.prepare(`
|
|
26
26
|
INSERT INTO session_navigation (${insertColumns.join(", ")})
|
|
27
27
|
VALUES (${insertColumns.map(() => "?").join(", ")})
|
|
28
28
|
ON CONFLICT(session_id) DO UPDATE SET ${assignments.join(", ")}
|
|
29
|
-
`).run(input.roomId ?? null, input.sessionId, input.rootSessionId ?? null, input.parentId ?? null, input.originId ?? null, input.title, input.profile, input.status, input.archivedAt ?? null, input.lastActivityAt, input.lastMessagePreview ?? null, input.childCount ?? 0, input.sortKey, input.updatedAt);
|
|
29
|
+
`).run(input.roomId ?? null, input.sessionId, input.rootSessionId ?? null, input.parentId ?? null, input.originId ?? null, input.title, input.profile, input.status ?? "running", input.archivedAt ?? null, input.lastActivityAt, input.lastMessagePreview ?? null, input.childCount ?? 0, input.sortKey, input.updatedAt);
|
|
30
30
|
const stored = this.getSession(input.sessionId);
|
|
31
31
|
if (!stored)
|
|
32
32
|
throw new Error(`Failed to upsert session navigation \"${input.sessionId}\"`);
|
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");
|