@pasko70/pibo 2.5.1 → 3.0.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/context/pibo-native-tooling.md +1 -0
- package/dist/agent-runtime/contract.js +2 -1
- package/dist/agent-runtime/history.js +53 -1
- package/dist/agent-runtime/routed-session.js +100 -26
- package/dist/agent-runtimes/codex-native/adapter.js +307 -31
- package/dist/agent-runtimes/codex-native/client.js +22 -3
- package/dist/agent-runtimes/codex-native/first-use.js +294 -0
- package/dist/agent-runtimes/codex-native/history.js +26 -7
- package/dist/agent-runtimes/codex-native/models.js +48 -1
- package/dist/agent-runtimes/codex-native/process.js +15 -1
- package/dist/agent-runtimes/codex-native/thread.js +75 -31
- package/dist/agent-runtimes/codex-native/turn.js +30 -0
- package/dist/agent-runtimes/history-proof.js +8 -0
- package/dist/agent-runtimes/omp/adapter.js +61 -14
- package/dist/agent-runtimes/omp/history.js +189 -34
- package/dist/agent-runtimes/omp/thread.js +82 -36
- package/dist/agent-runtimes/pi/adapter.js +30 -3
- package/dist/agent-runtimes/pi/history.js +66 -5
- package/dist/agent-runtimes/pi/openai-response-observer.js +102 -0
- package/dist/agent-runtimes/pi/routed-session.js +344 -37
- package/dist/apps/chat/agent-store.js +3 -0
- package/dist/apps/chat/chat-api-routes.js +1 -1
- package/dist/apps/chat/chat-files.js +130 -9
- package/dist/apps/chat/chat-request-normalizers.js +6 -0
- package/dist/apps/chat/chat-settings-routes.js +27 -2
- package/dist/apps/chat/chat-speech.js +100 -0
- package/dist/apps/chat/data/chat-data-mappers.js +25 -5
- package/dist/apps/chat/data/timeline-query-service.js +89 -1
- package/dist/apps/chat/trace-v2.js +210 -8
- package/dist/apps/chat/trace.js +3 -0
- package/dist/apps/chat/web-app.js +108 -3
- package/dist/apps/chat-ui/assets/{dist-GS8xUdfg.js → dist-BBDMeU5-.js} +1 -1
- package/dist/apps/chat-ui/assets/{dist-BpVSf6ZV.js → dist-BDIATCQt.js} +1 -1
- package/dist/apps/chat-ui/assets/{dist-CRv7fzi3.js → dist-JnW4v8UE.js} +1 -1
- package/dist/apps/chat-ui/assets/{dist-CbBXbGVi.js → dist-lRiLP9rr.js} +1 -1
- package/dist/apps/chat-ui/assets/{dist-HC0xQ92C.js → dist-mqCviI-c.js} +1 -1
- package/dist/apps/chat-ui/assets/index-0XOOVxLP.js +228 -0
- package/dist/apps/chat-ui/assets/index-DV7_CgsC.css +1 -0
- package/dist/apps/chat-ui/index.html +2 -2
- package/dist/apps/chat-vscode-web/assets/index-Dtw2Z7jz.js +43 -0
- package/dist/apps/chat-vscode-web/assets/{index-CF1-9PKU.css → index-SluZr_-r.css} +1 -1
- package/dist/apps/chat-vscode-web/index.html +2 -2
- package/dist/apps/vscode-artifacts/latest.vsix +0 -0
- package/dist/apps/vscode-artifacts/pibo-vscode-ext-3.0.0.vsix +0 -0
- package/dist/cli.js +49 -5
- package/dist/config/config.js +59 -8
- package/dist/core/preview-server-settings.js +20 -0
- package/dist/core/profiles.js +1 -0
- package/dist/core/session-router.js +476 -44
- package/dist/core/user-settings.js +10 -0
- package/dist/data/payload-store.js +57 -1
- package/dist/data/schema.js +18 -1
- package/dist/debug/trace.js +5 -0
- package/dist/gateway/client.js +368 -65
- package/dist/gateway/protocol.js +2 -0
- package/dist/gateway/server.js +8 -0
- package/dist/gateway/web.js +2 -0
- package/dist/index.js +1 -1
- package/dist/loops/store.js +6 -4
- package/dist/mcp/config.js +89 -17
- package/dist/mcp/daemon-client.js +473 -153
- package/dist/mcp/daemon.js +401 -37
- package/dist/plugins/builtin.js +2 -2
- package/dist/plugins/codex-native.js +2 -0
- package/dist/plugins/registry.js +260 -1
- package/dist/previews/cli.js +345 -0
- package/dist/previews/config.js +61 -0
- package/dist/previews/manager.js +644 -0
- package/dist/previews/network.js +198 -0
- package/dist/previews/plugin.js +11 -0
- package/dist/previews/process-supervisor.js +38 -0
- package/dist/previews/proxy.js +387 -0
- package/dist/previews/store.js +750 -0
- package/dist/previews/types.js +1 -0
- package/dist/previews/web-app.js +446 -0
- package/dist/runs/registry.js +22 -0
- package/dist/runs/tools.js +46 -7
- package/dist/session-ui/terminalRows.js +187 -13
- package/dist/sessions/pibo-data-store.js +37 -0
- package/dist/sessions/runtime-binding-persistence.js +34 -0
- package/dist/sessions/runtime-binding.js +6 -0
- package/dist/sessions/sqlite-store.js +37 -0
- package/dist/shared/trace-engine.js +12 -64
- package/dist/shared/trace-event-projection.js +16 -0
- package/dist/shared/trace-history.js +684 -184
- package/dist/shared/trace-limits.js +7 -0
- package/dist/shared/trace-live-patch.js +61 -0
- package/dist/shared/trace-tool-identity.js +43 -0
- package/dist/speech/openai-codex-realtime-call-proxy.js +248 -0
- package/dist/speech/openai-codex.js +407 -0
- package/dist/speech/types.js +8 -0
- package/dist/subagents/context.js +5 -3
- package/dist/subagents/observations.js +94 -6
- package/dist/subagents/tool.js +103 -20
- package/dist/tools/codex-image-artifacts.js +24 -0
- package/dist/tools/codex-image-generation.js +4 -22
- package/dist/web/channel.js +88 -4
- package/dist/web/http.js +2 -1
- package/npm-shrinkwrap.json +2 -2
- package/package.json +1 -1
- package/dist/apps/chat-ui/assets/index-0WZI2phJ.css +0 -1
- package/dist/apps/chat-ui/assets/index-DT5jCQBP.js +0 -228
- package/dist/apps/chat-vscode-web/assets/index-DvTSSvzN.js +0 -43
- package/dist/apps/vscode-artifacts/pibo-vscode-ext-2.5.1.vsix +0 -0
|
@@ -1,15 +1,18 @@
|
|
|
1
1
|
import { randomUUID } from "node:crypto";
|
|
2
|
+
import { isDeepStrictEqual } from "node:util";
|
|
2
3
|
import { InitialSessionContext, } from "./profiles.js";
|
|
3
4
|
import { createDefaultPiboPluginRegistry, createPiboProfileFromRegistryOrDefault, resolvePiboProfileNameFromRegistryOrDefault, selectDefaultPiboProfileName } from "../plugins/builtin.js";
|
|
4
5
|
import { RuntimeRoutedSession as RoutedSession, } from "../agent-runtime/routed-session.js";
|
|
5
6
|
import { runtimeSessionErrorDetails } from "./session-errors.js";
|
|
6
|
-
import {
|
|
7
|
+
import { normalizePiboAgentSessionName, } from "../subagents/tool.js";
|
|
8
|
+
import { PIBO_AGENT_OBSERVATION_DEFAULT_EVENT_TYPES, PIBO_AGENT_OBSERVATION_DEFAULT_TOOL_EVENT_TYPES, normalizePiboAgentObservationCursor, normalizePiboAgentObservationLimit, normalizePiboAgentObservationOrder, normalizePiboAgentObservationToolDetail, parsePiboAgentObservationTimestamp, piboAgentObservationDetails, piboAgentObservationKind, piboAgentObservationRole, piboAgentObservationSourceFromEvent, piboAgentObservationText, piboAgentObservationToolSummary, } from "../subagents/observations.js";
|
|
7
9
|
import { PiboRunRegistry } from "../runs/registry.js";
|
|
8
10
|
import { PiboRunCancellationError, PiboRunCancelledError, PiboRunExecutionTimeoutError, waitForRunCancellationSettlement } from "../runs/lifecycle.js";
|
|
9
11
|
import { PiboRunResourceLimitError } from "../runs/resource-isolation.js";
|
|
10
12
|
import { createPiboSignalRegistry } from "../signals/registry.js";
|
|
11
13
|
import { createDefaultPiboReliabilityStore } from "../reliability/store.js";
|
|
12
|
-
import { InMemoryPiboSessionStore, } from "../sessions/store.js";
|
|
14
|
+
import { InMemoryPiboSessionStore, createPiboSessionId, } from "../sessions/store.js";
|
|
15
|
+
import { createAgentRuntimeBindingPersistence } from "../sessions/runtime-binding-persistence.js";
|
|
13
16
|
import { createLegacyPiRuntimeSessionBinding, RuntimeSessionBindingConflictError, } from "../sessions/runtime-binding.js";
|
|
14
17
|
import { AgentRuntimeBindingMissingError, AgentRuntimeUnavailableError } from "../agent-runtime/errors.js";
|
|
15
18
|
import { validateAgentRuntimeProfileCapabilities } from "../agent-runtime/profile-validation.js";
|
|
@@ -52,6 +55,12 @@ export function resolvePiboSessionInitialFastMode(session) {
|
|
|
52
55
|
const value = session.metadata?.initialFastMode;
|
|
53
56
|
return typeof value === "boolean" ? value : undefined;
|
|
54
57
|
}
|
|
58
|
+
export function resolvePiboSessionInitialRuntimeOptions(session) {
|
|
59
|
+
const value = session.metadata?.initialRuntimeOptions;
|
|
60
|
+
return value && typeof value === "object" && !Array.isArray(value)
|
|
61
|
+
? structuredClone(value)
|
|
62
|
+
: undefined;
|
|
63
|
+
}
|
|
55
64
|
function hasReachedSubagentMaxDepth(subagent, depth) {
|
|
56
65
|
return depth >= (subagent.maxDepth ?? DEFAULT_SUBAGENT_MAX_DEPTH);
|
|
57
66
|
}
|
|
@@ -72,12 +81,14 @@ function subagentAbortError() {
|
|
|
72
81
|
function subagentRequestEventKey(piboSessionId, eventId) {
|
|
73
82
|
return `${piboSessionId}\u0000${eventId}`;
|
|
74
83
|
}
|
|
75
|
-
function profileForSession(baseProfile, runtimeInstanceId, nativeSessionId, parentNativeSessionId, subagentDepth) {
|
|
84
|
+
function profileForSession(baseProfile, runtimeInstanceId, nativeSessionId, parentNativeSessionId, subagentDepth, runtimeOptionsOverride) {
|
|
76
85
|
const usesProfileRuntime = baseProfile.runtimeInstanceId === runtimeInstanceId;
|
|
77
86
|
const options = {
|
|
78
87
|
profileName: baseProfile.profileName,
|
|
79
88
|
runtimeInstanceId,
|
|
80
|
-
runtimeOptions: usesProfileRuntime
|
|
89
|
+
runtimeOptions: usesProfileRuntime
|
|
90
|
+
? { ...baseProfile.runtimeOptions, ...runtimeOptionsOverride }
|
|
91
|
+
: {},
|
|
81
92
|
sessionId: nativeSessionId,
|
|
82
93
|
parentSessionId: parentNativeSessionId,
|
|
83
94
|
model: usesProfileRuntime ? baseProfile.model : undefined,
|
|
@@ -154,6 +165,23 @@ function formatRunReminderMessage(notification) {
|
|
|
154
165
|
function isRunReminderServiceMessage(event) {
|
|
155
166
|
return event.source === "service" && event.text.startsWith("<pibo_run_notification>");
|
|
156
167
|
}
|
|
168
|
+
function isRunReminderContextPressureError(event) {
|
|
169
|
+
return event.errorDetails?.category === "context_overflow"
|
|
170
|
+
|| event.errorDetails?.code === "context_length_exceeded"
|
|
171
|
+
|| event.errorDetails?.code === "run_reminder_limit_exceeded";
|
|
172
|
+
}
|
|
173
|
+
function runReminderRecoveryGroupKey(notification) {
|
|
174
|
+
if (notification.origin)
|
|
175
|
+
return `origin:${JSON.stringify(notification.origin)}`;
|
|
176
|
+
const runIds = [
|
|
177
|
+
...notification.completed,
|
|
178
|
+
...notification.failed,
|
|
179
|
+
...notification.timedOut,
|
|
180
|
+
...notification.cancelled,
|
|
181
|
+
...notification.running,
|
|
182
|
+
].map((run) => run.runId).sort();
|
|
183
|
+
return `runs:${JSON.stringify(runIds)}`;
|
|
184
|
+
}
|
|
157
185
|
function yieldedRunOrigin(event) {
|
|
158
186
|
if (!event?.id || event.provenance?.kind !== "loop-run")
|
|
159
187
|
return undefined;
|
|
@@ -182,6 +210,58 @@ function isTerminalRunStatus(status) {
|
|
|
182
210
|
function asJsonObject(value) {
|
|
183
211
|
return value ?? {};
|
|
184
212
|
}
|
|
213
|
+
const DERIVED_SESSION_RECONCILIATION_METADATA_KEY = "pibo.sessionIdentityReconciliation.v1";
|
|
214
|
+
function unresolvedDerivedSessionSource(session) {
|
|
215
|
+
const marker = session.metadata?.[DERIVED_SESSION_RECONCILIATION_METADATA_KEY];
|
|
216
|
+
if (!marker || typeof marker !== "object" || Array.isArray(marker))
|
|
217
|
+
return undefined;
|
|
218
|
+
return marker.state === "cleanup-required" && typeof marker.sourcePiboSessionId === "string"
|
|
219
|
+
? marker.sourcePiboSessionId
|
|
220
|
+
: undefined;
|
|
221
|
+
}
|
|
222
|
+
function derivedSessionMetadata(value) {
|
|
223
|
+
const metadata = { ...asJsonObject(value) };
|
|
224
|
+
for (const key of [
|
|
225
|
+
DERIVED_SESSION_RECONCILIATION_METADATA_KEY,
|
|
226
|
+
"workflowSessionKind",
|
|
227
|
+
"projectSessionKind",
|
|
228
|
+
"subagentName",
|
|
229
|
+
"subagentToolName",
|
|
230
|
+
"agentStatus",
|
|
231
|
+
"threadKey",
|
|
232
|
+
]) {
|
|
233
|
+
delete metadata[key];
|
|
234
|
+
}
|
|
235
|
+
return metadata;
|
|
236
|
+
}
|
|
237
|
+
function matchesDerivedSessionIntent(session, input) {
|
|
238
|
+
const binding = session.runtimeBinding;
|
|
239
|
+
const expectedBinding = input.runtimeBinding;
|
|
240
|
+
if (!binding || !expectedBinding)
|
|
241
|
+
return false;
|
|
242
|
+
const expectedPiSessionId = expectedBinding.adapterId === "pi" ? expectedBinding.nativeSessionId ?? "" : "";
|
|
243
|
+
return session.id === input.id
|
|
244
|
+
&& session.piSessionId === expectedPiSessionId
|
|
245
|
+
&& session.channel === input.channel
|
|
246
|
+
&& session.kind === input.kind
|
|
247
|
+
&& session.profile === input.profile
|
|
248
|
+
&& session.parentId === input.parentId
|
|
249
|
+
&& session.originId === input.originId
|
|
250
|
+
&& session.workspace === input.workspace
|
|
251
|
+
&& (session.title === input.title || (input.title === undefined && session.title === "Untitled Session"))
|
|
252
|
+
&& isDeepStrictEqual(session.metadata ?? {}, input.metadata ?? {})
|
|
253
|
+
&& isDeepStrictEqual(session.activeModel, input.activeModel)
|
|
254
|
+
&& binding.piboSessionId === input.id
|
|
255
|
+
&& binding.runtimeInstanceId === expectedBinding.runtimeInstanceId
|
|
256
|
+
&& binding.adapterId === expectedBinding.adapterId
|
|
257
|
+
&& binding.nativeSessionId === expectedBinding.nativeSessionId
|
|
258
|
+
&& binding.state === (expectedBinding.state ?? "unbound")
|
|
259
|
+
&& binding.protocol === expectedBinding.protocol
|
|
260
|
+
&& binding.protocolVersion === expectedBinding.protocolVersion
|
|
261
|
+
&& binding.adapterVersion === expectedBinding.adapterVersion
|
|
262
|
+
&& isDeepStrictEqual(binding.locator, expectedBinding.locator)
|
|
263
|
+
&& isDeepStrictEqual(binding.metadata ?? {}, expectedBinding.metadata ?? {});
|
|
264
|
+
}
|
|
185
265
|
function shouldResetSessionAfterAction(action, output) {
|
|
186
266
|
if (action === "login.apikey" || action === "logout")
|
|
187
267
|
return true;
|
|
@@ -265,10 +345,14 @@ export class PiboSessionRouter {
|
|
|
265
345
|
agentObservationEvictedThroughByParent = new Map();
|
|
266
346
|
nextAgentObservationSequence = 1;
|
|
267
347
|
scheduledRunReminders = new Map();
|
|
348
|
+
runReminderDeliveries = new Map();
|
|
349
|
+
deferredRunReminders = new Map();
|
|
350
|
+
runReminderRecoveries = new Map();
|
|
268
351
|
runReminderGenerations = new Map();
|
|
269
352
|
runCancellationHandlers = new Map();
|
|
270
353
|
activeRunExecutions = new Set();
|
|
271
354
|
quiescingSessions = new Set();
|
|
355
|
+
unresolvedDerivedSessionTransitions = new Map();
|
|
272
356
|
disposingSessions = new Map();
|
|
273
357
|
idleSessionTimers = new Map();
|
|
274
358
|
routedSessionIdleTimeoutMs;
|
|
@@ -348,6 +432,13 @@ export class PiboSessionRouter {
|
|
|
348
432
|
async emit(event) {
|
|
349
433
|
if (this.closing)
|
|
350
434
|
throw new Error("Pibo session router is disposed.");
|
|
435
|
+
let messageSignalAccepted = false;
|
|
436
|
+
const acceptMessageSignal = () => {
|
|
437
|
+
if (event.type !== "message" || !event.id || messageSignalAccepted)
|
|
438
|
+
return;
|
|
439
|
+
messageSignalAccepted = true;
|
|
440
|
+
this.signalRegistry.project({ type: "message_accepted", piboSessionId: event.piboSessionId, eventId: event.id, source: event.source });
|
|
441
|
+
};
|
|
351
442
|
const teardownAction = event.type === "execution" && (event.action === "dispose" || event.action === "kill" || event.action === "kill_all");
|
|
352
443
|
const teardownIds = teardownAction
|
|
353
444
|
? [event.piboSessionId, ...this.descendantSessionIds(event.piboSessionId)]
|
|
@@ -362,7 +453,13 @@ export class PiboSessionRouter {
|
|
|
362
453
|
const stored = this.sessionStore.get(event.piboSessionId);
|
|
363
454
|
if (stored)
|
|
364
455
|
this.signalRegistry.project({ type: "session_created", session: stored });
|
|
365
|
-
|
|
456
|
+
// Cold runtime creation and steering retain their established eager
|
|
457
|
+
// signal lifecycle. Cached queued messages are accepted inside the
|
|
458
|
+
// routed session's synchronous admission boundary so an identity
|
|
459
|
+
// reservation can reject without first publishing acceptance.
|
|
460
|
+
if (event.delivery === "steer" || (!this.sessions.has(event.piboSessionId) && !this.pendingSessions.has(event.piboSessionId))) {
|
|
461
|
+
acceptMessageSignal();
|
|
462
|
+
}
|
|
366
463
|
}
|
|
367
464
|
let session;
|
|
368
465
|
try {
|
|
@@ -391,9 +488,13 @@ export class PiboSessionRouter {
|
|
|
391
488
|
throw new Error(`Pibo session "${event.piboSessionId}" is quiescing.`);
|
|
392
489
|
}
|
|
393
490
|
if (event.type === "message") {
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
491
|
+
if (event.delivery === "steer")
|
|
492
|
+
return await session.steerMessage(event);
|
|
493
|
+
const output = session.enqueueMessage(event, acceptMessageSignal);
|
|
494
|
+
// Test doubles and compatibility shims may ignore the optional
|
|
495
|
+
// callback; production routed sessions invoke it before queueing.
|
|
496
|
+
acceptMessageSignal();
|
|
497
|
+
return output;
|
|
397
498
|
}
|
|
398
499
|
if (event.action === "abort") {
|
|
399
500
|
this.signalRegistry.project({ type: "session_interrupted", piboSessionId: event.piboSessionId, reason: "abort action" });
|
|
@@ -700,7 +801,7 @@ export class PiboSessionRouter {
|
|
|
700
801
|
&& parentBinding.adapterId === binding.adapterId
|
|
701
802
|
? parentBinding.nativeSessionId
|
|
702
803
|
: undefined;
|
|
703
|
-
return profileForSession(createPiboProfileFromRegistryOrDefault(this.pluginRegistry, session.profile), binding.runtimeInstanceId, binding.nativeSessionId, parentNativeSessionId, this.getSubagentDepth(session.id));
|
|
804
|
+
return profileForSession(createPiboProfileFromRegistryOrDefault(this.pluginRegistry, session.profile), binding.runtimeInstanceId, binding.nativeSessionId, parentNativeSessionId, this.getSubagentDepth(session.id), resolvePiboSessionInitialRuntimeOptions(session));
|
|
704
805
|
}
|
|
705
806
|
async rebindSessionRuntime(piboSessionId, input) {
|
|
706
807
|
if (this.quiescingSessions.has(piboSessionId)) {
|
|
@@ -748,7 +849,7 @@ export class PiboSessionRouter {
|
|
|
748
849
|
}
|
|
749
850
|
const workspace = session.workspace ?? this.options.cwd ?? getDefaultPiboWorkspace();
|
|
750
851
|
const baseProfile = createPiboProfileFromRegistryOrDefault(this.pluginRegistry, session.profile);
|
|
751
|
-
const targetProfile = profileForSession(baseProfile, input.runtimeInstanceId, startsNewNativeSession ? undefined : input.nativeSessionId, undefined, this.getSubagentDepth(session.id));
|
|
852
|
+
const targetProfile = profileForSession(baseProfile, input.runtimeInstanceId, startsNewNativeSession ? undefined : input.nativeSessionId, undefined, this.getSubagentDepth(session.id), resolvePiboSessionInitialRuntimeOptions(session));
|
|
752
853
|
const targetDiagnostics = await adapter.diagnose();
|
|
753
854
|
const unavailableTarget = targetDiagnostics.find((diagnostic) => diagnostic.severity === "error");
|
|
754
855
|
if (unavailableTarget) {
|
|
@@ -832,6 +933,15 @@ export class PiboSessionRouter {
|
|
|
832
933
|
this.scheduleIdleSessionEvictionIfIdle(piboSessionId);
|
|
833
934
|
}
|
|
834
935
|
}
|
|
936
|
+
async getSessionForkCandidates(piboSessionId) {
|
|
937
|
+
const session = await this.getOrCreateSession(piboSessionId);
|
|
938
|
+
try {
|
|
939
|
+
return await session.getForkCandidates();
|
|
940
|
+
}
|
|
941
|
+
finally {
|
|
942
|
+
this.scheduleIdleSessionEvictionIfIdle(piboSessionId);
|
|
943
|
+
}
|
|
944
|
+
}
|
|
835
945
|
async setLiveSessionActiveModel(piboSessionId, model) {
|
|
836
946
|
const session = this.sessions.get(piboSessionId);
|
|
837
947
|
if (!session)
|
|
@@ -1216,6 +1326,17 @@ export class PiboSessionRouter {
|
|
|
1216
1326
|
this.portableToolSessions.delete(piboSession.id);
|
|
1217
1327
|
throw error;
|
|
1218
1328
|
}
|
|
1329
|
+
const bindingSync = { expectedRevision: binding.revision };
|
|
1330
|
+
const runtimeBindingPersistence = createAgentRuntimeBindingPersistence(this.sessionStore, {
|
|
1331
|
+
piboSessionId: piboSession.id,
|
|
1332
|
+
onPersisted: (updated) => {
|
|
1333
|
+
binding = updated;
|
|
1334
|
+
bindingSync.expectedRevision = updated.revision;
|
|
1335
|
+
const updatedSession = this.sessionStore.get(piboSession.id);
|
|
1336
|
+
if (updatedSession)
|
|
1337
|
+
this.signalRegistry.project({ type: "session_created", session: updatedSession });
|
|
1338
|
+
},
|
|
1339
|
+
});
|
|
1219
1340
|
let runtimeSession;
|
|
1220
1341
|
try {
|
|
1221
1342
|
runtimeSession = await runtimeRegistry.openAgentRuntimeSession(binding.runtimeInstanceId, {
|
|
@@ -1241,6 +1362,7 @@ export class PiboSessionRouter {
|
|
|
1241
1362
|
codeRuntimeToolController,
|
|
1242
1363
|
portableTools,
|
|
1243
1364
|
resources,
|
|
1365
|
+
...(runtimeBindingPersistence ? { runtimeBindingPersistence } : {}),
|
|
1244
1366
|
compatibility: {
|
|
1245
1367
|
persistSession: this.options.persistSession,
|
|
1246
1368
|
thinkingLevel: initialThinkingLevel ?? this.options.thinkingLevel,
|
|
@@ -1301,6 +1423,7 @@ export class PiboSessionRouter {
|
|
|
1301
1423
|
binding = this.persistSessionRuntimeBinding(piboSession, openedBinding, {
|
|
1302
1424
|
expectedRevision: binding.revision,
|
|
1303
1425
|
});
|
|
1426
|
+
bindingSync.expectedRevision = binding.revision;
|
|
1304
1427
|
}
|
|
1305
1428
|
}
|
|
1306
1429
|
catch (error) {
|
|
@@ -1323,6 +1446,7 @@ export class PiboSessionRouter {
|
|
|
1323
1446
|
})
|
|
1324
1447
|
: undefined,
|
|
1325
1448
|
onSessionOperation: (result, event) => this.handleSessionOperation(result, event),
|
|
1449
|
+
onBeforeSessionIdentityOperation: (event) => this.retryUnresolvedDerivedSessionCompensation(event.piboSessionId),
|
|
1326
1450
|
onKillChildren: (id, opts) => this.killChildSessions(id, opts),
|
|
1327
1451
|
onStateChange: (state) => {
|
|
1328
1452
|
this.signalRegistry.project({
|
|
@@ -1331,18 +1455,22 @@ export class PiboSessionRouter {
|
|
|
1331
1455
|
processing: state.processing,
|
|
1332
1456
|
queuedMessages: state.queuedMessages,
|
|
1333
1457
|
});
|
|
1334
|
-
if (!state.processing && state.queuedMessages === 0 && !state.disposed) {
|
|
1335
|
-
this.syncLiveSessionRuntimeBinding(piboSession.id, runtimeSession);
|
|
1458
|
+
if (!state.processing && state.queuedMessages === 0 && !state.disposed && !state.sessionIdentityOperationInFlight) {
|
|
1459
|
+
this.syncLiveSessionRuntimeBinding(piboSession.id, runtimeSession, bindingSync);
|
|
1460
|
+
this.scheduleRunReminder(piboSession.id, false);
|
|
1336
1461
|
}
|
|
1337
|
-
if (state.disposed || state.processing || state.queuedMessages > 0)
|
|
1462
|
+
if (state.disposed || state.processing || state.queuedMessages > 0 || state.sessionIdentityOperationInFlight)
|
|
1338
1463
|
this.clearIdleSessionTimer(piboSession.id);
|
|
1339
1464
|
else
|
|
1340
1465
|
this.scheduleIdleSessionEvictionIfIdle(piboSession.id);
|
|
1341
1466
|
},
|
|
1342
|
-
onMessagesInterrupted: (messages, reason) =>
|
|
1343
|
-
|
|
1344
|
-
|
|
1345
|
-
|
|
1467
|
+
onMessagesInterrupted: (messages, reason) => {
|
|
1468
|
+
this.telemetryRecorder?.recordMessagesInterrupted(messages, {
|
|
1469
|
+
session: this.sessionStore.get(piboSession.id),
|
|
1470
|
+
status: this.sessions.get(piboSession.id)?.getStatus(),
|
|
1471
|
+
}, reason);
|
|
1472
|
+
this.handleInterruptedRunReminders(messages);
|
|
1473
|
+
},
|
|
1346
1474
|
messagePreflight: this.options.messagePreflight,
|
|
1347
1475
|
getRuntimeAuthStatus: () => runtimeRegistry.getAgentRuntimeAuthStatus(binding.runtimeInstanceId),
|
|
1348
1476
|
startRuntimeAuth: async (input) => {
|
|
@@ -1422,22 +1550,33 @@ export class PiboSessionRouter {
|
|
|
1422
1550
|
updatedAt: now,
|
|
1423
1551
|
};
|
|
1424
1552
|
}
|
|
1425
|
-
syncLiveSessionRuntimeBinding(piboSessionId, runtimeSession) {
|
|
1553
|
+
syncLiveSessionRuntimeBinding(piboSessionId, runtimeSession, state) {
|
|
1426
1554
|
const session = this.sessionStore.get(piboSessionId);
|
|
1427
1555
|
if (!session)
|
|
1428
1556
|
return;
|
|
1429
1557
|
try {
|
|
1430
1558
|
const persisted = this.resolveSessionRuntimeBinding(session);
|
|
1431
|
-
this.persistSessionRuntimeBinding(session, withPersistedPortableHistoryAuditMetadata(persisted, runtimeSession.getBinding()));
|
|
1559
|
+
const updated = this.persistSessionRuntimeBinding(session, withPersistedPortableHistoryAuditMetadata(persisted, runtimeSession.getBinding()), { expectedRevision: state.expectedRevision });
|
|
1560
|
+
state.expectedRevision = updated.revision;
|
|
1432
1561
|
}
|
|
1433
1562
|
catch (error) {
|
|
1434
1563
|
const message = error instanceof Error ? error.message : String(error);
|
|
1564
|
+
const reset = this.resetCachedSession(piboSessionId, "runtime binding synchronization failed");
|
|
1435
1565
|
this.emitOutput({
|
|
1436
1566
|
type: "session_error",
|
|
1437
1567
|
piboSessionId,
|
|
1438
1568
|
error: `Failed to persist the live runtime binding: ${message}`,
|
|
1439
1569
|
errorDetails: runtimeSessionErrorDetails(message),
|
|
1440
1570
|
});
|
|
1571
|
+
void reset.catch((resetError) => {
|
|
1572
|
+
const resetMessage = resetError instanceof Error ? resetError.message : String(resetError);
|
|
1573
|
+
this.emitOutput({
|
|
1574
|
+
type: "session_error",
|
|
1575
|
+
piboSessionId,
|
|
1576
|
+
error: `Failed to discard a runtime after binding synchronization failed: ${resetMessage}`,
|
|
1577
|
+
errorDetails: runtimeSessionErrorDetails(resetMessage),
|
|
1578
|
+
});
|
|
1579
|
+
});
|
|
1441
1580
|
}
|
|
1442
1581
|
}
|
|
1443
1582
|
assertOpenableRuntimeBinding(binding) {
|
|
@@ -1486,21 +1625,44 @@ export class PiboSessionRouter {
|
|
|
1486
1625
|
const source = this.resolvePiboSession(event.piboSessionId);
|
|
1487
1626
|
const previousBinding = this.resolveSessionRuntimeBinding(source);
|
|
1488
1627
|
const liveBinding = this.sessions.get(event.piboSessionId)?.getRuntimeBinding();
|
|
1628
|
+
const currentNativeSessionId = result.current.piSessionId || undefined;
|
|
1489
1629
|
const currentBinding = {
|
|
1490
1630
|
...previousBinding,
|
|
1491
1631
|
...liveBinding,
|
|
1492
1632
|
piboSessionId: source.id,
|
|
1493
|
-
nativeSessionId:
|
|
1494
|
-
state: "bound",
|
|
1495
|
-
locator: result.current.sessionFile
|
|
1633
|
+
nativeSessionId: currentNativeSessionId,
|
|
1634
|
+
state: currentNativeSessionId ? "bound" : "unbound",
|
|
1635
|
+
locator: currentNativeSessionId && result.current.sessionFile
|
|
1496
1636
|
? { kind: "local-file", value: result.current.sessionFile }
|
|
1497
|
-
:
|
|
1637
|
+
: currentNativeSessionId
|
|
1638
|
+
? liveBinding?.locator ?? previousBinding.locator
|
|
1639
|
+
: undefined,
|
|
1640
|
+
metadata: currentNativeSessionId ? liveBinding?.metadata ?? previousBinding.metadata : undefined,
|
|
1498
1641
|
};
|
|
1499
1642
|
if (event.action === "session.fork" || event.action === "session.clone") {
|
|
1500
1643
|
const action = event.action;
|
|
1501
|
-
|
|
1502
|
-
|
|
1503
|
-
|
|
1644
|
+
let transitionError;
|
|
1645
|
+
try {
|
|
1646
|
+
const created = this.createDerivedSession(result, action, currentBinding);
|
|
1647
|
+
result.piboSessionId = created.id;
|
|
1648
|
+
}
|
|
1649
|
+
catch (error) {
|
|
1650
|
+
transitionError = error;
|
|
1651
|
+
}
|
|
1652
|
+
// Always discard the source live handle after derivation. Bound native
|
|
1653
|
+
// branches move that handle to the derived session; first-message Codex
|
|
1654
|
+
// branches intentionally leave it on the source and persist unbound.
|
|
1655
|
+
try {
|
|
1656
|
+
await this.resetCachedSession(event.piboSessionId);
|
|
1657
|
+
}
|
|
1658
|
+
catch (resetError) {
|
|
1659
|
+
if (transitionError) {
|
|
1660
|
+
throw new AggregateError([transitionError, resetError], `Failed to persist and reset ${action}.`);
|
|
1661
|
+
}
|
|
1662
|
+
throw resetError;
|
|
1663
|
+
}
|
|
1664
|
+
if (transitionError)
|
|
1665
|
+
throw transitionError;
|
|
1504
1666
|
return;
|
|
1505
1667
|
}
|
|
1506
1668
|
this.persistSessionRuntimeBinding(source, currentBinding, {
|
|
@@ -1511,17 +1673,18 @@ export class PiboSessionRouter {
|
|
|
1511
1673
|
}
|
|
1512
1674
|
createDerivedSession(result, action, currentBinding) {
|
|
1513
1675
|
const source = this.resolvePiboSession(result.piboSessionId);
|
|
1514
|
-
|
|
1676
|
+
const input = {
|
|
1677
|
+
id: createPiboSessionId(),
|
|
1515
1678
|
channel: source.channel,
|
|
1516
1679
|
kind: "branch",
|
|
1517
1680
|
profile: source.profile,
|
|
1518
|
-
parentId: source.
|
|
1681
|
+
parentId: source.parentId,
|
|
1519
1682
|
originId: source.id,
|
|
1520
1683
|
runtimeBinding: {
|
|
1521
1684
|
runtimeInstanceId: currentBinding.runtimeInstanceId,
|
|
1522
1685
|
adapterId: currentBinding.adapterId,
|
|
1523
|
-
nativeSessionId: currentBinding.nativeSessionId
|
|
1524
|
-
state:
|
|
1686
|
+
nativeSessionId: currentBinding.nativeSessionId,
|
|
1687
|
+
state: currentBinding.state,
|
|
1525
1688
|
protocol: currentBinding.protocol,
|
|
1526
1689
|
protocolVersion: currentBinding.protocolVersion,
|
|
1527
1690
|
adapterVersion: currentBinding.adapterVersion,
|
|
@@ -1532,12 +1695,116 @@ export class PiboSessionRouter {
|
|
|
1532
1695
|
title: source.title,
|
|
1533
1696
|
activeModel: source.activeModel,
|
|
1534
1697
|
metadata: {
|
|
1535
|
-
...
|
|
1698
|
+
...derivedSessionMetadata(source.metadata),
|
|
1536
1699
|
originAction: action,
|
|
1537
1700
|
originRuntimeNativeSessionId: result.previous.piSessionId,
|
|
1538
1701
|
...(currentBinding.adapterId === "pi" ? { originPiSessionId: result.previous.piSessionId } : {}),
|
|
1539
1702
|
},
|
|
1703
|
+
};
|
|
1704
|
+
let created;
|
|
1705
|
+
try {
|
|
1706
|
+
created = this.sessionStore.create(input);
|
|
1707
|
+
}
|
|
1708
|
+
catch (error) {
|
|
1709
|
+
let persisted;
|
|
1710
|
+
try {
|
|
1711
|
+
persisted = this.sessionStore.get(input.id);
|
|
1712
|
+
}
|
|
1713
|
+
catch (lookupError) {
|
|
1714
|
+
const failure = new AggregateError([error, lookupError], `Branch persistence for "${input.id}" failed and its commit state could not be reconciled.`);
|
|
1715
|
+
this.unresolvedDerivedSessionTransitions.set(source.id, { piboSessionId: input.id, cause: failure });
|
|
1716
|
+
throw failure;
|
|
1717
|
+
}
|
|
1718
|
+
if (!persisted)
|
|
1719
|
+
throw error;
|
|
1720
|
+
if (matchesDerivedSessionIntent(persisted, input))
|
|
1721
|
+
return persisted;
|
|
1722
|
+
return this.rejectDerivedSessionAfterCompensation(source.id, input.id, error);
|
|
1723
|
+
}
|
|
1724
|
+
if (matchesDerivedSessionIntent(created, input))
|
|
1725
|
+
return created;
|
|
1726
|
+
return this.rejectDerivedSessionAfterCompensation(source.id, input.id, new Error(`Session store returned a branch that does not match the intended derivation "${input.id}".`));
|
|
1727
|
+
}
|
|
1728
|
+
retryUnresolvedDerivedSessionCompensation(sourcePiboSessionId) {
|
|
1729
|
+
const remembered = this.unresolvedDerivedSessionTransitions.get(sourcePiboSessionId);
|
|
1730
|
+
const persisted = this.sessionStore.find({ originId: sourcePiboSessionId })
|
|
1731
|
+
.filter((session) => unresolvedDerivedSessionSource(session) === sourcePiboSessionId)
|
|
1732
|
+
.map((session) => ({
|
|
1733
|
+
piboSessionId: session.id,
|
|
1734
|
+
cause: new Error(`Branch "${session.id}" has a persisted session identity reconciliation marker.`),
|
|
1735
|
+
}));
|
|
1736
|
+
const unresolved = [
|
|
1737
|
+
...(remembered ? [remembered] : []),
|
|
1738
|
+
...persisted.filter((candidate) => candidate.piboSessionId !== remembered?.piboSessionId),
|
|
1739
|
+
];
|
|
1740
|
+
for (const candidate of unresolved) {
|
|
1741
|
+
try {
|
|
1742
|
+
if (!this.sessionStore.get(candidate.piboSessionId)) {
|
|
1743
|
+
if (remembered?.piboSessionId === candidate.piboSessionId) {
|
|
1744
|
+
this.unresolvedDerivedSessionTransitions.delete(sourcePiboSessionId);
|
|
1745
|
+
}
|
|
1746
|
+
continue;
|
|
1747
|
+
}
|
|
1748
|
+
if (!this.sessionStore.delete)
|
|
1749
|
+
throw new Error("Session store does not support compensating deletion.");
|
|
1750
|
+
this.sessionStore.delete(candidate.piboSessionId);
|
|
1751
|
+
if (this.sessionStore.get(candidate.piboSessionId)) {
|
|
1752
|
+
throw new Error(`Compensating deletion left branch "${candidate.piboSessionId}" persisted.`);
|
|
1753
|
+
}
|
|
1754
|
+
if (remembered?.piboSessionId === candidate.piboSessionId) {
|
|
1755
|
+
this.unresolvedDerivedSessionTransitions.delete(sourcePiboSessionId);
|
|
1756
|
+
}
|
|
1757
|
+
}
|
|
1758
|
+
catch (error) {
|
|
1759
|
+
this.unresolvedDerivedSessionTransitions.set(sourcePiboSessionId, candidate);
|
|
1760
|
+
throw new AggregateError([candidate.cause, error], `Branch "${candidate.piboSessionId}" still requires compensation; refusing another session identity operation for source "${sourcePiboSessionId}".`);
|
|
1761
|
+
}
|
|
1762
|
+
}
|
|
1763
|
+
}
|
|
1764
|
+
persistDerivedSessionReconciliationMarker(sourcePiboSessionId, piboSessionId) {
|
|
1765
|
+
const residual = this.sessionStore.get(piboSessionId);
|
|
1766
|
+
if (!residual)
|
|
1767
|
+
return;
|
|
1768
|
+
const updated = this.sessionStore.update(piboSessionId, {
|
|
1769
|
+
metadata: {
|
|
1770
|
+
...(residual.metadata ?? {}),
|
|
1771
|
+
[DERIVED_SESSION_RECONCILIATION_METADATA_KEY]: {
|
|
1772
|
+
state: "cleanup-required",
|
|
1773
|
+
sourcePiboSessionId,
|
|
1774
|
+
},
|
|
1775
|
+
},
|
|
1540
1776
|
});
|
|
1777
|
+
if (!updated || unresolvedDerivedSessionSource(updated) !== sourcePiboSessionId) {
|
|
1778
|
+
throw new Error(`Failed to persist the reconciliation marker for branch "${piboSessionId}".`);
|
|
1779
|
+
}
|
|
1780
|
+
}
|
|
1781
|
+
rejectDerivedSessionAfterCompensation(sourcePiboSessionId, piboSessionId, cause) {
|
|
1782
|
+
let cleanupError;
|
|
1783
|
+
try {
|
|
1784
|
+
if (!this.sessionStore.delete) {
|
|
1785
|
+
throw new Error("Session store does not support compensating deletion.");
|
|
1786
|
+
}
|
|
1787
|
+
this.sessionStore.delete(piboSessionId);
|
|
1788
|
+
if (this.sessionStore.get(piboSessionId)) {
|
|
1789
|
+
throw new Error(`Compensating deletion left branch "${piboSessionId}" persisted.`);
|
|
1790
|
+
}
|
|
1791
|
+
}
|
|
1792
|
+
catch (error) {
|
|
1793
|
+
cleanupError = error;
|
|
1794
|
+
}
|
|
1795
|
+
if (cleanupError) {
|
|
1796
|
+
let markerError;
|
|
1797
|
+
try {
|
|
1798
|
+
this.persistDerivedSessionReconciliationMarker(sourcePiboSessionId, piboSessionId);
|
|
1799
|
+
}
|
|
1800
|
+
catch (error) {
|
|
1801
|
+
markerError = error;
|
|
1802
|
+
}
|
|
1803
|
+
const failure = new AggregateError([cause, cleanupError, ...(markerError ? [markerError] : [])], `Branch persistence for "${piboSessionId}" was inconsistent and compensation failed.`);
|
|
1804
|
+
this.unresolvedDerivedSessionTransitions.set(sourcePiboSessionId, { piboSessionId, cause: failure });
|
|
1805
|
+
throw failure;
|
|
1806
|
+
}
|
|
1807
|
+
throw cause;
|
|
1541
1808
|
}
|
|
1542
1809
|
runtimeAuthAffectedInstanceIds(runtimeInstanceId) {
|
|
1543
1810
|
const registry = this.resolveAgentRuntimeRegistry(runtimeInstanceId);
|
|
@@ -1653,13 +1920,14 @@ export class PiboSessionRouter {
|
|
|
1653
1920
|
}
|
|
1654
1921
|
createAgentsController(parentPiboSessionId) {
|
|
1655
1922
|
return {
|
|
1656
|
-
sendMessage: async ({ subagent, message, threadKey, toolCallId, requestId, parentProvenance, signal }) => {
|
|
1923
|
+
sendMessage: async ({ subagent, sessionName, message, threadKey, toolCallId, requestId, parentProvenance, signal }) => {
|
|
1657
1924
|
if (signal?.aborted)
|
|
1658
1925
|
throw subagentAbortError();
|
|
1659
1926
|
if (typeof requestId !== "string" || !requestId.trim())
|
|
1660
1927
|
throw new Error("Delegated agent requestId is required.");
|
|
1661
1928
|
this.assertSubagentDepth(parentPiboSessionId, subagent);
|
|
1662
|
-
const
|
|
1929
|
+
const normalizedSessionName = normalizePiboAgentSessionName(sessionName);
|
|
1930
|
+
const child = this.resolveSubagentSession(parentPiboSessionId, subagent, normalizedSessionName, threadKey);
|
|
1663
1931
|
const resolvedThreadKey = typeof child.metadata?.threadKey === "string" ? child.metadata.threadKey : "";
|
|
1664
1932
|
const loopJobId = parentProvenance?.kind === "loop-run"
|
|
1665
1933
|
? parentProvenance.jobId
|
|
@@ -1754,6 +2022,7 @@ export class PiboSessionRouter {
|
|
|
1754
2022
|
agentId: session.id,
|
|
1755
2023
|
name: typeof session.metadata?.subagentName === "string" ? session.metadata.subagentName : session.profile,
|
|
1756
2024
|
profile: session.profile,
|
|
2025
|
+
...(session.title ? { sessionName: session.title } : {}),
|
|
1757
2026
|
...(typeof session.metadata?.threadKey === "string" ? { threadKey: session.metadata.threadKey } : {}),
|
|
1758
2027
|
status: killed ? "killed" : running ? "running" : "idle",
|
|
1759
2028
|
createdAt: session.createdAt,
|
|
@@ -1775,6 +2044,7 @@ export class PiboSessionRouter {
|
|
|
1775
2044
|
observeManagedAgents(parentPiboSessionId, input) {
|
|
1776
2045
|
const order = normalizePiboAgentObservationOrder(input.order);
|
|
1777
2046
|
const limit = normalizePiboAgentObservationLimit(input.limit);
|
|
2047
|
+
const toolDetail = normalizePiboAgentObservationToolDetail(input.toolDetail);
|
|
1778
2048
|
const afterSequence = normalizePiboAgentObservationCursor(input.afterSequence);
|
|
1779
2049
|
const since = parsePiboAgentObservationTimestamp(input.since, "since");
|
|
1780
2050
|
const until = parsePiboAgentObservationTimestamp(input.until, "until");
|
|
@@ -1793,6 +2063,14 @@ export class PiboSessionRouter {
|
|
|
1793
2063
|
const kinds = input.kinds ? new Set(input.kinds) : undefined;
|
|
1794
2064
|
const roles = input.roles ? new Set(input.roles) : undefined;
|
|
1795
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);
|
|
1796
2074
|
const matches = this.agentObservations.filter((observation) => {
|
|
1797
2075
|
if (observation.managingParentId !== parentPiboSessionId)
|
|
1798
2076
|
return false;
|
|
@@ -1804,6 +2082,10 @@ export class PiboSessionRouter {
|
|
|
1804
2082
|
return false;
|
|
1805
2083
|
if (threadKeys && (!observation.threadKey || !threadKeys.has(observation.threadKey)))
|
|
1806
2084
|
return false;
|
|
2085
|
+
if (observation.kind === "tool" && !includeTools)
|
|
2086
|
+
return false;
|
|
2087
|
+
if (defaultMessageView && !defaultEventTypeSet.has(observation.eventType))
|
|
2088
|
+
return false;
|
|
1807
2089
|
if (eventTypes && !eventTypes.has(observation.eventType))
|
|
1808
2090
|
return false;
|
|
1809
2091
|
if (kinds && !kinds.has(observation.kind))
|
|
@@ -1829,7 +2111,10 @@ export class PiboSessionRouter {
|
|
|
1829
2111
|
ordered.reverse();
|
|
1830
2112
|
const selected = ordered.map((observation) => {
|
|
1831
2113
|
const { managingParentId: _managingParentId, details, ...visible } = observation;
|
|
1832
|
-
|
|
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;
|
|
1833
2118
|
});
|
|
1834
2119
|
const evictedThrough = this.agentObservationEvictedThroughByParent.get(parentPiboSessionId) ?? 0;
|
|
1835
2120
|
const retentionTruncated = afterSequence === undefined ? evictedThrough > 0 : afterSequence < evictedThrough;
|
|
@@ -1837,9 +2122,12 @@ export class PiboSessionRouter {
|
|
|
1837
2122
|
return {
|
|
1838
2123
|
filters: {
|
|
1839
2124
|
...input,
|
|
2125
|
+
...(defaultMessageView ? { eventTypes: defaultEventTypes } : {}),
|
|
1840
2126
|
...(afterSequence !== undefined ? { afterSequence } : {}),
|
|
1841
2127
|
order,
|
|
1842
2128
|
limit,
|
|
2129
|
+
includeTools,
|
|
2130
|
+
toolDetail,
|
|
1843
2131
|
includeDetails: input.includeDetails === true,
|
|
1844
2132
|
},
|
|
1845
2133
|
observations: selected,
|
|
@@ -2042,7 +2330,7 @@ export class PiboSessionRouter {
|
|
|
2042
2330
|
}
|
|
2043
2331
|
return depth;
|
|
2044
2332
|
}
|
|
2045
|
-
resolveSubagentSession(parentPiboSessionId, subagent, threadKey) {
|
|
2333
|
+
resolveSubagentSession(parentPiboSessionId, subagent, sessionName, threadKey) {
|
|
2046
2334
|
const targetProfile = resolvePiboProfileNameFromRegistryOrDefault(this.pluginRegistry, subagent.targetProfile);
|
|
2047
2335
|
const parent = this.resolvePiboSession(parentPiboSessionId);
|
|
2048
2336
|
const resolvedThreadKey = resolveSubagentThreadKey(threadKey);
|
|
@@ -2058,9 +2346,13 @@ export class PiboSessionRouter {
|
|
|
2058
2346
|
const parentChatRoomId = typeof parent.metadata?.chatRoomId === "string" ? parent.metadata.chatRoomId : undefined;
|
|
2059
2347
|
if (parentChatRoomId)
|
|
2060
2348
|
metadata.chatRoomId = parentChatRoomId;
|
|
2061
|
-
const newSessionMetadata =
|
|
2062
|
-
|
|
2063
|
-
:
|
|
2349
|
+
const newSessionMetadata = {
|
|
2350
|
+
...metadata,
|
|
2351
|
+
...(subagent.thinkingLevel ? { initialThinkingLevel: subagent.thinkingLevel } : {}),
|
|
2352
|
+
...(subagent.runtimeOptions && Object.keys(subagent.runtimeOptions).length > 0
|
|
2353
|
+
? { initialRuntimeOptions: structuredClone(subagent.runtimeOptions) }
|
|
2354
|
+
: {}),
|
|
2355
|
+
};
|
|
2064
2356
|
const existing = this.sessionStore.find({
|
|
2065
2357
|
channel: "pibo.subagents",
|
|
2066
2358
|
kind: "subagent",
|
|
@@ -2075,8 +2367,8 @@ export class PiboSessionRouter {
|
|
|
2075
2367
|
agentStatus: "active",
|
|
2076
2368
|
...(parentChatRoomId ? { chatRoomId: parentChatRoomId } : {}),
|
|
2077
2369
|
}, "subagent");
|
|
2078
|
-
if (JSON.stringify(updatedMetadata) !== JSON.stringify(existing.metadata ?? {})) {
|
|
2079
|
-
return this.sessionStore.update(existing.id, { metadata: updatedMetadata }) ?? existing;
|
|
2370
|
+
if (existing.title !== sessionName || JSON.stringify(updatedMetadata) !== JSON.stringify(existing.metadata ?? {})) {
|
|
2371
|
+
return this.sessionStore.update(existing.id, { title: sessionName, metadata: updatedMetadata }) ?? existing;
|
|
2080
2372
|
}
|
|
2081
2373
|
return existing;
|
|
2082
2374
|
}
|
|
@@ -2088,6 +2380,7 @@ export class PiboSessionRouter {
|
|
|
2088
2380
|
parentId: parent.id,
|
|
2089
2381
|
runtimeBinding: this.createRuntimeBindingInput(childProfile),
|
|
2090
2382
|
workspace: parent.workspace,
|
|
2383
|
+
title: sessionName,
|
|
2091
2384
|
metadata: newSessionMetadata,
|
|
2092
2385
|
activeModel: subagent.model,
|
|
2093
2386
|
});
|
|
@@ -2150,10 +2443,130 @@ export class PiboSessionRouter {
|
|
|
2150
2443
|
for (const listener of this.listeners) {
|
|
2151
2444
|
listener(event);
|
|
2152
2445
|
}
|
|
2446
|
+
this.handleRunReminderOutput(event);
|
|
2153
2447
|
if (event.type === "message_finished" && event.source !== "service") {
|
|
2154
2448
|
this.scheduleRunReminder(event.piboSessionId, true);
|
|
2155
2449
|
}
|
|
2156
2450
|
};
|
|
2451
|
+
handleRunReminderOutput(event) {
|
|
2452
|
+
if (event.type === "message_finished") {
|
|
2453
|
+
const delivery = event.eventId ? this.runReminderDeliveries.get(event.eventId) : undefined;
|
|
2454
|
+
if (event.eventId)
|
|
2455
|
+
this.runReminderDeliveries.delete(event.eventId);
|
|
2456
|
+
if (delivery) {
|
|
2457
|
+
this.clearRunReminderRecovery(delivery);
|
|
2458
|
+
this.scheduleRunReminder(delivery.piboSessionId, false, delivery.generation);
|
|
2459
|
+
}
|
|
2460
|
+
return;
|
|
2461
|
+
}
|
|
2462
|
+
if (event.type === "session_error") {
|
|
2463
|
+
const eventId = event.eventId;
|
|
2464
|
+
if (!eventId)
|
|
2465
|
+
return;
|
|
2466
|
+
const delivery = this.runReminderDeliveries.get(eventId);
|
|
2467
|
+
if (!delivery)
|
|
2468
|
+
return;
|
|
2469
|
+
this.runReminderDeliveries.delete(eventId);
|
|
2470
|
+
if (!isRunReminderContextPressureError(event)
|
|
2471
|
+
|| this.closing
|
|
2472
|
+
|| this.quiescingSessions.has(delivery.piboSessionId)
|
|
2473
|
+
|| delivery.generation !== this.runReminderGeneration(delivery.piboSessionId)) {
|
|
2474
|
+
this.runRegistry.releaseNotification(delivery.piboSessionId, delivery.notification);
|
|
2475
|
+
return;
|
|
2476
|
+
}
|
|
2477
|
+
if (this.hasRunReminderRecovery(delivery)) {
|
|
2478
|
+
const released = this.runRegistry.releaseNotification(delivery.piboSessionId, delivery.notification);
|
|
2479
|
+
for (const run of released)
|
|
2480
|
+
this.runRegistry.suppressNotification(delivery.piboSessionId, run.runId);
|
|
2481
|
+
this.clearRunReminderRecovery(delivery);
|
|
2482
|
+
this.deferredRunReminders.delete(delivery.piboSessionId);
|
|
2483
|
+
this.sessions.get(delivery.piboSessionId)?.removeQueuedMessages(isRunReminderServiceMessage);
|
|
2484
|
+
this.scheduleRunReminder(delivery.piboSessionId, false, delivery.generation);
|
|
2485
|
+
return;
|
|
2486
|
+
}
|
|
2487
|
+
this.runRegistry.releaseNotification(delivery.piboSessionId, delivery.notification);
|
|
2488
|
+
if (!this.runRegistry.hasPendingNotification(delivery.piboSessionId))
|
|
2489
|
+
return;
|
|
2490
|
+
this.recordRunReminderRecovery(delivery);
|
|
2491
|
+
const alreadyDeferred = this.deferredRunReminders.get(delivery.piboSessionId) === delivery.generation;
|
|
2492
|
+
this.deferredRunReminders.set(delivery.piboSessionId, delivery.generation);
|
|
2493
|
+
this.sessions.get(delivery.piboSessionId)?.removeQueuedMessages(isRunReminderServiceMessage);
|
|
2494
|
+
if (!alreadyDeferred)
|
|
2495
|
+
this.queueRunReminderRecoveryCompaction(delivery.piboSessionId, delivery.generation);
|
|
2496
|
+
return;
|
|
2497
|
+
}
|
|
2498
|
+
if (event.type !== "compaction_end" || event.aborted || event.errorMessage)
|
|
2499
|
+
return;
|
|
2500
|
+
const generation = this.deferredRunReminders.get(event.piboSessionId);
|
|
2501
|
+
if (generation === undefined)
|
|
2502
|
+
return;
|
|
2503
|
+
this.deferredRunReminders.delete(event.piboSessionId);
|
|
2504
|
+
if (this.closing || this.quiescingSessions.has(event.piboSessionId))
|
|
2505
|
+
return;
|
|
2506
|
+
if (generation !== this.runReminderGeneration(event.piboSessionId))
|
|
2507
|
+
return;
|
|
2508
|
+
this.scheduleRunReminder(event.piboSessionId, true, generation);
|
|
2509
|
+
}
|
|
2510
|
+
hasRunReminderRecovery(delivery) {
|
|
2511
|
+
const recovery = this.runReminderRecoveries.get(delivery.piboSessionId);
|
|
2512
|
+
return recovery?.generation === delivery.generation
|
|
2513
|
+
&& recovery.groups.has(runReminderRecoveryGroupKey(delivery.notification));
|
|
2514
|
+
}
|
|
2515
|
+
recordRunReminderRecovery(delivery) {
|
|
2516
|
+
let recovery = this.runReminderRecoveries.get(delivery.piboSessionId);
|
|
2517
|
+
if (!recovery || recovery.generation !== delivery.generation) {
|
|
2518
|
+
recovery = { generation: delivery.generation, groups: new Set() };
|
|
2519
|
+
this.runReminderRecoveries.set(delivery.piboSessionId, recovery);
|
|
2520
|
+
}
|
|
2521
|
+
recovery.groups.add(runReminderRecoveryGroupKey(delivery.notification));
|
|
2522
|
+
}
|
|
2523
|
+
clearRunReminderRecovery(delivery) {
|
|
2524
|
+
const recovery = this.runReminderRecoveries.get(delivery.piboSessionId);
|
|
2525
|
+
if (!recovery || recovery.generation !== delivery.generation)
|
|
2526
|
+
return;
|
|
2527
|
+
recovery.groups.delete(runReminderRecoveryGroupKey(delivery.notification));
|
|
2528
|
+
if (recovery.groups.size === 0)
|
|
2529
|
+
this.runReminderRecoveries.delete(delivery.piboSessionId);
|
|
2530
|
+
}
|
|
2531
|
+
queueRunReminderRecoveryCompaction(piboSessionId, generation) {
|
|
2532
|
+
const session = this.sessions.get(piboSessionId);
|
|
2533
|
+
if (!session)
|
|
2534
|
+
return;
|
|
2535
|
+
const eventId = randomUUID();
|
|
2536
|
+
void session.executeAction({
|
|
2537
|
+
type: "execution",
|
|
2538
|
+
piboSessionId,
|
|
2539
|
+
id: eventId,
|
|
2540
|
+
action: "compact",
|
|
2541
|
+
params: {
|
|
2542
|
+
customInstructions: "Preserve the current task and pending yielded-run lifecycle, and leave enough context for the deferred run notification.",
|
|
2543
|
+
},
|
|
2544
|
+
}).catch((error) => {
|
|
2545
|
+
if (this.closing || this.quiescingSessions.has(piboSessionId))
|
|
2546
|
+
return;
|
|
2547
|
+
if (generation !== this.runReminderGeneration(piboSessionId))
|
|
2548
|
+
return;
|
|
2549
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
2550
|
+
this.emitOutput({
|
|
2551
|
+
type: "session_error",
|
|
2552
|
+
piboSessionId,
|
|
2553
|
+
eventId,
|
|
2554
|
+
error: message,
|
|
2555
|
+
errorDetails: runtimeSessionErrorDetails(message),
|
|
2556
|
+
});
|
|
2557
|
+
});
|
|
2558
|
+
}
|
|
2559
|
+
handleInterruptedRunReminders(messages) {
|
|
2560
|
+
for (const message of messages) {
|
|
2561
|
+
if (!isRunReminderServiceMessage(message) || !message.id)
|
|
2562
|
+
continue;
|
|
2563
|
+
const delivery = this.runReminderDeliveries.get(message.id);
|
|
2564
|
+
if (!delivery)
|
|
2565
|
+
continue;
|
|
2566
|
+
this.runReminderDeliveries.delete(message.id);
|
|
2567
|
+
this.runRegistry.releaseNotification(delivery.piboSessionId, delivery.notification);
|
|
2568
|
+
}
|
|
2569
|
+
}
|
|
2157
2570
|
projectKnownSessionSignals() {
|
|
2158
2571
|
for (const session of this.sessionStore.list?.() ?? []) {
|
|
2159
2572
|
this.signalRegistry.project({ type: "session_created", session });
|
|
@@ -2173,6 +2586,12 @@ export class PiboSessionRouter {
|
|
|
2173
2586
|
for (const piboSessionId of piboSessionIds) {
|
|
2174
2587
|
this.runReminderGenerations.set(piboSessionId, this.runReminderGeneration(piboSessionId) + 1);
|
|
2175
2588
|
this.scheduledRunReminders.delete(piboSessionId);
|
|
2589
|
+
this.deferredRunReminders.delete(piboSessionId);
|
|
2590
|
+
this.runReminderRecoveries.delete(piboSessionId);
|
|
2591
|
+
for (const [eventId, delivery] of this.runReminderDeliveries) {
|
|
2592
|
+
if (delivery.piboSessionId === piboSessionId)
|
|
2593
|
+
this.runReminderDeliveries.delete(eventId);
|
|
2594
|
+
}
|
|
2176
2595
|
try {
|
|
2177
2596
|
this.sessions.get(piboSessionId)?.removeQueuedMessages(isRunReminderServiceMessage);
|
|
2178
2597
|
}
|
|
@@ -2201,6 +2620,11 @@ export class PiboSessionRouter {
|
|
|
2201
2620
|
return;
|
|
2202
2621
|
if (expectedGeneration !== this.runReminderGeneration(piboSessionId))
|
|
2203
2622
|
return;
|
|
2623
|
+
const deferredGeneration = this.deferredRunReminders.get(piboSessionId);
|
|
2624
|
+
if (deferredGeneration === expectedGeneration)
|
|
2625
|
+
return;
|
|
2626
|
+
if (deferredGeneration !== undefined)
|
|
2627
|
+
this.deferredRunReminders.delete(piboSessionId);
|
|
2204
2628
|
if (!this.runRegistry.hasPendingNotification(piboSessionId, { includeAlreadyNotified }))
|
|
2205
2629
|
return;
|
|
2206
2630
|
const previous = this.scheduledRunReminders.get(piboSessionId);
|
|
@@ -2228,23 +2652,31 @@ export class PiboSessionRouter {
|
|
|
2228
2652
|
this.scheduledRunReminders.delete(piboSessionId);
|
|
2229
2653
|
if (this.closing || this.quiescingSessions.has(piboSessionId) || expectedGeneration !== this.runReminderGeneration(piboSessionId))
|
|
2230
2654
|
return;
|
|
2231
|
-
|
|
2232
|
-
|
|
2233
|
-
return;
|
|
2655
|
+
let notification;
|
|
2656
|
+
let eventId;
|
|
2234
2657
|
try {
|
|
2235
2658
|
const session = await this.getOrCreateSession(piboSessionId);
|
|
2236
2659
|
if (this.closing || this.quiescingSessions.has(piboSessionId) || expectedGeneration !== this.runReminderGeneration(piboSessionId))
|
|
2237
2660
|
return;
|
|
2661
|
+
notification = this.runRegistry.createNotification(piboSessionId, { includeAlreadyNotified: scheduled.includeAlreadyNotified });
|
|
2662
|
+
if (!notification)
|
|
2663
|
+
return;
|
|
2664
|
+
eventId = randomUUID();
|
|
2665
|
+
this.runReminderDeliveries.set(eventId, { piboSessionId, generation: expectedGeneration, notification });
|
|
2238
2666
|
session.enqueueMessage({
|
|
2239
2667
|
type: "message",
|
|
2240
2668
|
piboSessionId,
|
|
2241
2669
|
text: formatRunReminderMessage(notification),
|
|
2242
2670
|
source: "service",
|
|
2243
|
-
id:
|
|
2671
|
+
id: eventId,
|
|
2244
2672
|
provenance: runReminderProvenance(notification),
|
|
2245
2673
|
});
|
|
2246
2674
|
}
|
|
2247
2675
|
catch (error) {
|
|
2676
|
+
if (eventId)
|
|
2677
|
+
this.runReminderDeliveries.delete(eventId);
|
|
2678
|
+
if (notification)
|
|
2679
|
+
this.runRegistry.releaseNotification(piboSessionId, notification);
|
|
2248
2680
|
if (this.closing || this.quiescingSessions.has(piboSessionId) || expectedGeneration !== this.runReminderGeneration(piboSessionId))
|
|
2249
2681
|
return;
|
|
2250
2682
|
const message = error instanceof Error ? error.message : String(error);
|