@pasko70/pibo 2.4.1 → 2.4.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/agent-runtime/context-build.js +100 -11
- package/dist/agent-runtime/profile-validation.js +3 -0
- package/dist/agent-runtime/resource-service.js +16 -0
- package/dist/agent-runtime/routed-session.js +180 -30
- package/dist/agent-runtime/testing/fake-adapter.js +7 -0
- package/dist/agent-runtimes/pi/adapter.js +2 -0
- package/dist/agent-runtimes/pi/routed-session.js +104 -43
- package/dist/agent-runtimes/pi/runtime.js +8 -5
- package/dist/apps/chat/web-app.js +21 -6
- package/dist/apps/chat-ui/assets/{dist-Cw9po47P.js → dist-C4JGcQjh.js} +1 -1
- package/dist/apps/chat-ui/assets/{dist-BeqHbnGN.js → dist-CF92Lv76.js} +1 -1
- package/dist/apps/chat-ui/assets/{dist-DTRjeLwO.js → dist-CJ0JS-bE.js} +1 -1
- package/dist/apps/chat-ui/assets/{dist-3YG57JXi.js → dist-DrgKyb9n.js} +1 -1
- package/dist/apps/chat-ui/assets/{dist-CrDtveZB.js → dist-VoMyV-PE.js} +1 -1
- package/dist/apps/chat-ui/assets/index-0WZI2phJ.css +1 -0
- package/dist/apps/chat-ui/assets/index-DhX1_aRM.js +228 -0
- package/dist/apps/chat-ui/index.html +2 -2
- package/dist/apps/vscode-artifacts/latest.vsix +0 -0
- package/dist/apps/vscode-artifacts/{pibo-vscode-ext-2.4.1.vsix → pibo-vscode-ext-2.4.3.vsix} +0 -0
- package/dist/cli.js +16 -6
- package/dist/core/context-build.js +66 -6
- package/dist/core/model-defaults.js +11 -3
- package/dist/core/session-router.js +285 -112
- package/dist/gateway/server.js +1 -0
- package/dist/loops/accounting.js +80 -0
- package/dist/loops/service.js +52 -13
- package/dist/loops/store.js +67 -19
- package/dist/loops/tools.js +3 -1
- package/dist/reliability/store.js +11 -6
- package/dist/runs/lifecycle.js +38 -1
- package/dist/runs/registry.js +46 -45
- package/dist/runs/tools.js +34 -24
- package/dist/subagents/context.js +48 -0
- package/dist/subagents/runtime-selection.js +28 -0
- package/dist/subagents/tool.js +28 -6
- package/dist/tools/codex-compat.js +1 -0
- package/dist/tools/contract.js +10 -0
- package/dist/tools/mcp-bridge.js +4 -2
- package/dist/tools/runtime/node-backend.js +8 -2
- package/dist/tools/runtime/python-backend.js +8 -2
- package/dist/tools/runtime/tool.js +1 -0
- package/dist/tools/session-tool-set.js +19 -12
- package/npm-shrinkwrap.json +2 -2
- package/package.json +1 -1
- package/dist/apps/chat-ui/assets/index-AjnP3ci-.js +0 -228
- package/dist/apps/chat-ui/assets/index-BJ56TREg.css +0 -1
|
@@ -5,7 +5,7 @@ import { RuntimeRoutedSession as RoutedSession, } from "../agent-runtime/routed-
|
|
|
5
5
|
import { runtimeSessionErrorDetails } from "./session-errors.js";
|
|
6
6
|
import { normalizePiboAgentObservationCursor, normalizePiboAgentObservationLimit, normalizePiboAgentObservationOrder, parsePiboAgentObservationTimestamp, piboAgentObservationDetails, piboAgentObservationKind, piboAgentObservationRole, piboAgentObservationSourceFromEvent, piboAgentObservationText, } from "../subagents/observations.js";
|
|
7
7
|
import { PiboRunRegistry } from "../runs/registry.js";
|
|
8
|
-
import { PiboRunExecutionTimeoutError } from "../runs/lifecycle.js";
|
|
8
|
+
import { PiboRunCancellationError, PiboRunCancelledError, PiboRunExecutionTimeoutError, waitForRunCancellationSettlement } from "../runs/lifecycle.js";
|
|
9
9
|
import { PiboRunResourceLimitError } from "../runs/resource-isolation.js";
|
|
10
10
|
import { createPiboSignalRegistry } from "../signals/registry.js";
|
|
11
11
|
import { createDefaultPiboReliabilityStore } from "../reliability/store.js";
|
|
@@ -29,7 +29,6 @@ import { createPiboToolPayloadWriter } from "../tools/payload-writer.js";
|
|
|
29
29
|
import { PiboPortableToolService, } from "../tools/session-service.js";
|
|
30
30
|
import { PiboRuntimeResourceService, } from "../agent-runtime/resource-service.js";
|
|
31
31
|
import { PORTABLE_HISTORY_HANDOFF_METADATA_KEY, PORTABLE_HISTORY_LAST_IMPORT_METADATA_KEY, PiboDataPortableHistoryProvider, createPortableHistoryHandoffMetadata, readPortableHistoryHandoffMetadata, withoutPortableHistoryHandoffMetadata, withPortableHistoryHandoffMetadata, } from "../agent-runtime/portable-history.js";
|
|
32
|
-
const DEFAULT_SUBAGENT_REPLY_TIMEOUT_MS = 10 * 60 * 1000;
|
|
33
32
|
const DEFAULT_SUBAGENT_MAX_DEPTH = 1;
|
|
34
33
|
const MAX_SUBAGENT_THREAD_KEY_BYTES = 512;
|
|
35
34
|
const MAX_AGENT_OBSERVATIONS = 5_000;
|
|
@@ -70,6 +69,9 @@ function subagentAbortError() {
|
|
|
70
69
|
error.name = "AbortError";
|
|
71
70
|
return error;
|
|
72
71
|
}
|
|
72
|
+
function subagentRequestEventKey(piboSessionId, eventId) {
|
|
73
|
+
return `${piboSessionId}\u0000${eventId}`;
|
|
74
|
+
}
|
|
73
75
|
function profileForSession(baseProfile, runtimeInstanceId, nativeSessionId, parentNativeSessionId, subagentDepth) {
|
|
74
76
|
const usesProfileRuntime = baseProfile.runtimeInstanceId === runtimeInstanceId;
|
|
75
77
|
const options = {
|
|
@@ -150,7 +152,29 @@ function formatRunReminderMessage(notification) {
|
|
|
150
152
|
].join("\n");
|
|
151
153
|
}
|
|
152
154
|
function isRunReminderServiceMessage(event) {
|
|
153
|
-
return event.source === "service" && event.
|
|
155
|
+
return event.source === "service" && event.text.startsWith("<pibo_run_notification>");
|
|
156
|
+
}
|
|
157
|
+
function yieldedRunOrigin(event) {
|
|
158
|
+
if (!event?.id || event.provenance?.kind !== "loop-run")
|
|
159
|
+
return undefined;
|
|
160
|
+
return {
|
|
161
|
+
eventId: event.provenance.rootEventId ?? event.id,
|
|
162
|
+
provenance: {
|
|
163
|
+
kind: event.provenance.kind,
|
|
164
|
+
jobId: event.provenance.jobId,
|
|
165
|
+
runId: event.provenance.runId,
|
|
166
|
+
},
|
|
167
|
+
};
|
|
168
|
+
}
|
|
169
|
+
function runReminderProvenance(notification) {
|
|
170
|
+
const origin = notification.origin;
|
|
171
|
+
if (!origin || origin.provenance.kind !== "loop-run")
|
|
172
|
+
return undefined;
|
|
173
|
+
return {
|
|
174
|
+
...origin.provenance,
|
|
175
|
+
cause: "run-reminder",
|
|
176
|
+
rootEventId: origin.eventId,
|
|
177
|
+
};
|
|
154
178
|
}
|
|
155
179
|
function isTerminalRunStatus(status) {
|
|
156
180
|
return status === "completed" || status === "failed" || status === "timed_out" || status === "cancelled";
|
|
@@ -235,13 +259,15 @@ export class PiboSessionRouter {
|
|
|
235
259
|
portableHistoryProvider;
|
|
236
260
|
runtimeResourceSessions = new Map();
|
|
237
261
|
runtimeAuthFingerprints = new Map();
|
|
238
|
-
|
|
262
|
+
activeSubagentRequests = new Map();
|
|
263
|
+
subagentRequestIdsByEvent = new Map();
|
|
239
264
|
agentObservations = [];
|
|
240
265
|
agentObservationEvictedThroughByParent = new Map();
|
|
241
266
|
nextAgentObservationSequence = 1;
|
|
242
267
|
scheduledRunReminders = new Map();
|
|
243
268
|
runReminderGenerations = new Map();
|
|
244
269
|
runCancellationHandlers = new Map();
|
|
270
|
+
activeRunExecutions = new Set();
|
|
245
271
|
quiescingSessions = new Set();
|
|
246
272
|
disposingSessions = new Map();
|
|
247
273
|
idleSessionTimers = new Map();
|
|
@@ -388,11 +414,24 @@ export class PiboSessionRouter {
|
|
|
388
414
|
teardownCompleted = true;
|
|
389
415
|
return output;
|
|
390
416
|
}
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
417
|
+
let output;
|
|
418
|
+
if (event.action === "abort") {
|
|
419
|
+
const [sessionAbort, childAbort] = await Promise.allSettled([
|
|
420
|
+
session.executeAction(event),
|
|
421
|
+
this.abortActiveSubagentSessions(event.piboSessionId),
|
|
422
|
+
]);
|
|
423
|
+
if (sessionAbort.status === "rejected" && childAbort.status === "rejected") {
|
|
424
|
+
throw new AggregateError([sessionAbort.reason, childAbort.reason], "Failed to abort the session and its active subagent requests.");
|
|
425
|
+
}
|
|
426
|
+
if (sessionAbort.status === "rejected")
|
|
427
|
+
throw sessionAbort.reason;
|
|
428
|
+
if (childAbort.status === "rejected")
|
|
429
|
+
throw childAbort.reason;
|
|
430
|
+
output = sessionAbort.value;
|
|
431
|
+
}
|
|
432
|
+
else {
|
|
433
|
+
output = await session.executeAction(event);
|
|
434
|
+
}
|
|
396
435
|
if (event.action === "kill" || event.action === "kill_all") {
|
|
397
436
|
await this.disposeSessionSubtree(event.piboSessionId, `${event.action} action`, { cancelRuns: event.action === "kill_all" });
|
|
398
437
|
teardownCompleted = true;
|
|
@@ -446,24 +485,24 @@ export class PiboSessionRouter {
|
|
|
446
485
|
const failures = [];
|
|
447
486
|
for (const id of ids) {
|
|
448
487
|
const session = this.sessions.get(id);
|
|
449
|
-
if (session)
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
catch (error) {
|
|
455
|
-
failures.push(error);
|
|
456
|
-
}
|
|
488
|
+
if (!session)
|
|
489
|
+
continue;
|
|
490
|
+
this.signalRegistry.project({ type: "session_disposed", piboSessionId: id, reason: "kill" });
|
|
491
|
+
try {
|
|
492
|
+
killed.push(await session.kill());
|
|
457
493
|
}
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
494
|
+
catch (error) {
|
|
495
|
+
failures.push(error);
|
|
496
|
+
}
|
|
497
|
+
}
|
|
498
|
+
if (options?.includeRuns) {
|
|
499
|
+
const runs = ids.flatMap((id) => this.runRegistry.listActiveControllerRuns(id));
|
|
500
|
+
try {
|
|
501
|
+
const cancelled = await this.cancelRunsAfterSettlement(runs, "Pibo session subtree was killed.");
|
|
502
|
+
cancelledRuns.push(...cancelled.map((run) => run.runId));
|
|
503
|
+
}
|
|
504
|
+
catch (error) {
|
|
505
|
+
failures.push(error);
|
|
467
506
|
}
|
|
468
507
|
}
|
|
469
508
|
try {
|
|
@@ -520,10 +559,9 @@ export class PiboSessionRouter {
|
|
|
520
559
|
const operation = (async () => {
|
|
521
560
|
await startGate;
|
|
522
561
|
const runCancellationResults = options.cancelRuns
|
|
523
|
-
? await Promise.allSettled(
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
}))
|
|
562
|
+
? await Promise.allSettled([
|
|
563
|
+
this.cancelRunsAfterSettlement(ids.flatMap((id) => this.runRegistry.listActiveControllerRuns(id)), reason),
|
|
564
|
+
])
|
|
527
565
|
: [];
|
|
528
566
|
const pending = ids.map((id) => this.pendingSessions.get(id)).filter((value) => Boolean(value));
|
|
529
567
|
if (pending.length > 0)
|
|
@@ -597,11 +635,11 @@ export class PiboSessionRouter {
|
|
|
597
635
|
const childSession = this.sessions.get(id);
|
|
598
636
|
if (childSession)
|
|
599
637
|
killed.push(await childSession.kill());
|
|
600
|
-
if (options?.includeRuns)
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
638
|
+
if (!options?.includeRuns)
|
|
639
|
+
continue;
|
|
640
|
+
const runs = this.runRegistry.listActiveControllerRuns(id);
|
|
641
|
+
const cancelled = await this.cancelRunsAfterSettlement(runs, `Child Pibo session "${id}" was killed.`);
|
|
642
|
+
cancelledRuns.push(...cancelled.map((run) => run.runId));
|
|
605
643
|
}
|
|
606
644
|
return { killed, cancelledRuns };
|
|
607
645
|
}
|
|
@@ -652,6 +690,18 @@ export class PiboSessionRouter {
|
|
|
652
690
|
const session = this.sessionStore.get(piboSessionId);
|
|
653
691
|
return session ? structuredClone(this.resolveSessionRuntimeBinding(session)) : undefined;
|
|
654
692
|
}
|
|
693
|
+
getSessionRuntimeProfile(piboSessionId) {
|
|
694
|
+
const session = this.resolvePiboSession(piboSessionId);
|
|
695
|
+
const binding = this.resolveSessionRuntimeBinding(session);
|
|
696
|
+
const parent = session.parentId ? this.resolvePiboSession(session.parentId) : undefined;
|
|
697
|
+
const parentBinding = parent ? this.resolveSessionRuntimeBinding(parent) : undefined;
|
|
698
|
+
const parentNativeSessionId = parentBinding
|
|
699
|
+
&& parentBinding.runtimeInstanceId === binding.runtimeInstanceId
|
|
700
|
+
&& parentBinding.adapterId === binding.adapterId
|
|
701
|
+
? parentBinding.nativeSessionId
|
|
702
|
+
: undefined;
|
|
703
|
+
return profileForSession(createPiboProfileFromRegistryOrDefault(this.pluginRegistry, session.profile), binding.runtimeInstanceId, binding.nativeSessionId, parentNativeSessionId, this.getSubagentDepth(session.id));
|
|
704
|
+
}
|
|
655
705
|
async rebindSessionRuntime(piboSessionId, input) {
|
|
656
706
|
if (this.quiescingSessions.has(piboSessionId)) {
|
|
657
707
|
throw new Error(`Pibo session "${piboSessionId}" is already quiescing.`);
|
|
@@ -833,24 +883,14 @@ export class PiboSessionRouter {
|
|
|
833
883
|
subscribeSignalStatuses(listener) {
|
|
834
884
|
return this.signalRegistry.subscribeAll(listener);
|
|
835
885
|
}
|
|
836
|
-
async emitMessageAndWaitForReply(event, timeoutMs
|
|
886
|
+
async emitMessageAndWaitForReply(event, timeoutMs, signal) {
|
|
837
887
|
const eventWithId = { ...event, id: event.id ?? randomUUID() };
|
|
838
888
|
return await new Promise((resolve, reject) => {
|
|
839
889
|
let settled = false;
|
|
840
|
-
let
|
|
890
|
+
let dispatchPromise;
|
|
841
891
|
let lastAssistantMessage;
|
|
842
892
|
let timeout;
|
|
843
|
-
const
|
|
844
|
-
if (!messageDispatched)
|
|
845
|
-
return;
|
|
846
|
-
void this.emit({
|
|
847
|
-
type: "execution",
|
|
848
|
-
piboSessionId: eventWithId.piboSessionId,
|
|
849
|
-
action: "abort",
|
|
850
|
-
id: randomUUID(),
|
|
851
|
-
}).catch(() => { });
|
|
852
|
-
};
|
|
853
|
-
const finish = (result) => {
|
|
893
|
+
const claimSettlement = () => {
|
|
854
894
|
if (settled)
|
|
855
895
|
return false;
|
|
856
896
|
settled = true;
|
|
@@ -858,16 +898,33 @@ export class PiboSessionRouter {
|
|
|
858
898
|
clearTimeout(timeout);
|
|
859
899
|
signal?.removeEventListener("abort", onAbort);
|
|
860
900
|
unsubscribe();
|
|
901
|
+
return true;
|
|
902
|
+
};
|
|
903
|
+
const finish = (result) => {
|
|
904
|
+
if (!claimSettlement())
|
|
905
|
+
return;
|
|
861
906
|
if (result instanceof Error)
|
|
862
907
|
reject(result);
|
|
863
908
|
else
|
|
864
909
|
resolve(result);
|
|
865
|
-
return true;
|
|
866
910
|
};
|
|
867
|
-
const
|
|
868
|
-
if (!
|
|
911
|
+
const rejectAfterMessageCancellation = (error) => {
|
|
912
|
+
if (!claimSettlement())
|
|
869
913
|
return;
|
|
870
|
-
|
|
914
|
+
void (async () => {
|
|
915
|
+
try {
|
|
916
|
+
await dispatchPromise;
|
|
917
|
+
await this.cancelSessionMessage(eventWithId.piboSessionId, eventWithId.id);
|
|
918
|
+
}
|
|
919
|
+
catch (cancellationError) {
|
|
920
|
+
reject(new PiboRunCancellationError(`Failed to cancel subagent request "${eventWithId.id}" in Pibo session "${eventWithId.piboSessionId}".`, { cause: cancellationError }));
|
|
921
|
+
return;
|
|
922
|
+
}
|
|
923
|
+
reject(error);
|
|
924
|
+
})();
|
|
925
|
+
};
|
|
926
|
+
const onAbort = () => {
|
|
927
|
+
rejectAfterMessageCancellation(subagentAbortError());
|
|
871
928
|
};
|
|
872
929
|
const unsubscribe = this.subscribe((output) => {
|
|
873
930
|
if (output.piboSessionId !== eventWithId.piboSessionId ||
|
|
@@ -886,20 +943,27 @@ export class PiboSessionRouter {
|
|
|
886
943
|
}
|
|
887
944
|
});
|
|
888
945
|
if (signal?.aborted) {
|
|
889
|
-
|
|
946
|
+
finish(subagentAbortError());
|
|
890
947
|
return;
|
|
891
948
|
}
|
|
892
949
|
signal?.addEventListener("abort", onAbort, { once: true });
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
950
|
+
if (timeoutMs !== undefined) {
|
|
951
|
+
timeout = setTimeout(() => {
|
|
952
|
+
rejectAfterMessageCancellation(new PiboRunExecutionTimeoutError(`Timed out waiting for assistant reply from Pibo session "${eventWithId.piboSessionId}"`, "lifetime"));
|
|
953
|
+
}, timeoutMs);
|
|
954
|
+
}
|
|
955
|
+
dispatchPromise = this.emit(eventWithId);
|
|
956
|
+
dispatchPromise.catch((error) => {
|
|
957
|
+
finish(error instanceof Error ? error : new Error(String(error)));
|
|
958
|
+
});
|
|
901
959
|
});
|
|
902
960
|
}
|
|
961
|
+
async cancelSessionMessage(piboSessionId, eventId) {
|
|
962
|
+
const session = this.sessions.get(piboSessionId);
|
|
963
|
+
if (!session || !await session.cancelMessage(eventId)) {
|
|
964
|
+
throw new Error(`Pibo session "${piboSessionId}" no longer owns message "${eventId}".`);
|
|
965
|
+
}
|
|
966
|
+
}
|
|
903
967
|
async disposeAll() {
|
|
904
968
|
if (this.disposePromise)
|
|
905
969
|
return this.disposePromise;
|
|
@@ -916,8 +980,9 @@ export class PiboSessionRouter {
|
|
|
916
980
|
for (const timer of this.idleSessionTimers.values())
|
|
917
981
|
clearTimeout(timer);
|
|
918
982
|
this.idleSessionTimers.clear();
|
|
919
|
-
const
|
|
920
|
-
|
|
983
|
+
const runCancellationResult = await Promise.allSettled([
|
|
984
|
+
this.cancelRunsAfterSettlement(this.runRegistry.listActiveRuns(), "Pibo session router was disposed."),
|
|
985
|
+
]);
|
|
921
986
|
this.scheduledRunReminders.clear();
|
|
922
987
|
const closeResult = await Promise.allSettled([this.runtimeRegistry.closeAll({ force: true })]);
|
|
923
988
|
const disposeResults = await Promise.allSettled(sessions.map(([id, session]) => this.disposeRoutedSession(id, session, "router disposed")));
|
|
@@ -946,7 +1011,8 @@ export class PiboSessionRouter {
|
|
|
946
1011
|
await this.portableToolService.dispose();
|
|
947
1012
|
await this.runtimeResourceService.dispose();
|
|
948
1013
|
this.runtimeResourceSessions.clear();
|
|
949
|
-
this.
|
|
1014
|
+
this.activeSubagentRequests.clear();
|
|
1015
|
+
this.subagentRequestIdsByEvent.clear();
|
|
950
1016
|
this.agentObservations.length = 0;
|
|
951
1017
|
this.agentObservationEvictedThroughByParent.clear();
|
|
952
1018
|
await this.telemetryWriter?.dispose();
|
|
@@ -1031,19 +1097,13 @@ export class PiboSessionRouter {
|
|
|
1031
1097
|
const piboSession = this.resolvePiboSession(piboSessionId);
|
|
1032
1098
|
let session;
|
|
1033
1099
|
this.signalRegistry.project({ type: "session_created", session: piboSession });
|
|
1034
|
-
const profile = createPiboProfileFromRegistryOrDefault(this.pluginRegistry, piboSession.profile);
|
|
1035
1100
|
let binding = this.resolveSessionRuntimeBinding(piboSession);
|
|
1036
1101
|
const parent = piboSession.parentId ? this.resolvePiboSession(piboSession.parentId) : undefined;
|
|
1037
1102
|
const parentBinding = parent ? this.resolveSessionRuntimeBinding(parent) : undefined;
|
|
1038
1103
|
const parentModelScopeId = parent ? parentBinding?.nativeSessionId ?? parent.id : undefined;
|
|
1039
|
-
const runtimeParentNativeSessionId = parentBinding
|
|
1040
|
-
&& parentBinding.runtimeInstanceId === binding.runtimeInstanceId
|
|
1041
|
-
&& parentBinding.adapterId === binding.adapterId
|
|
1042
|
-
? parentBinding.nativeSessionId
|
|
1043
|
-
: undefined;
|
|
1044
1104
|
const modelDefaults = this.resolveModelDefaults();
|
|
1045
1105
|
const initialThinkingLevel = resolvePiboSessionInitialThinkingLevel(piboSession);
|
|
1046
|
-
const sessionProfile =
|
|
1106
|
+
const sessionProfile = this.getSessionRuntimeProfile(piboSession.id);
|
|
1047
1107
|
const persistedHistoryHandoff = readPortableHistoryHandoffMetadata(binding.metadata);
|
|
1048
1108
|
if (binding.metadata?.[PORTABLE_HISTORY_HANDOFF_METADATA_KEY] !== undefined && !persistedHistoryHandoff) {
|
|
1049
1109
|
throw new Error("The pending portable history handoff metadata is invalid; refusing to start a contextless target runtime.");
|
|
@@ -1562,77 +1622,118 @@ export class PiboSessionRouter {
|
|
|
1562
1622
|
this.signalRegistry.project({ type: "session_created", session: created });
|
|
1563
1623
|
return created;
|
|
1564
1624
|
}
|
|
1565
|
-
trackActiveSubagent(parentPiboSessionId,
|
|
1566
|
-
let
|
|
1567
|
-
if (!
|
|
1568
|
-
|
|
1569
|
-
this.
|
|
1625
|
+
trackActiveSubagent(parentPiboSessionId, request) {
|
|
1626
|
+
let requests = this.activeSubagentRequests.get(parentPiboSessionId);
|
|
1627
|
+
if (!requests) {
|
|
1628
|
+
requests = new Set();
|
|
1629
|
+
this.activeSubagentRequests.set(parentPiboSessionId, requests);
|
|
1570
1630
|
}
|
|
1571
|
-
|
|
1631
|
+
requests.add(request);
|
|
1572
1632
|
let active = true;
|
|
1573
1633
|
return () => {
|
|
1574
1634
|
if (!active)
|
|
1575
1635
|
return;
|
|
1576
1636
|
active = false;
|
|
1577
|
-
const current = this.
|
|
1637
|
+
const current = this.activeSubagentRequests.get(parentPiboSessionId);
|
|
1578
1638
|
if (!current)
|
|
1579
1639
|
return;
|
|
1580
|
-
|
|
1581
|
-
if (remaining > 0)
|
|
1582
|
-
current.set(childPiboSessionId, remaining);
|
|
1583
|
-
else
|
|
1584
|
-
current.delete(childPiboSessionId);
|
|
1640
|
+
current.delete(request);
|
|
1585
1641
|
if (current.size === 0)
|
|
1586
|
-
this.
|
|
1642
|
+
this.activeSubagentRequests.delete(parentPiboSessionId);
|
|
1587
1643
|
};
|
|
1588
1644
|
}
|
|
1589
1645
|
async abortActiveSubagentSessions(parentPiboSessionId) {
|
|
1590
|
-
const
|
|
1591
|
-
|
|
1592
|
-
|
|
1593
|
-
await Promise.
|
|
1594
|
-
|
|
1595
|
-
|
|
1596
|
-
|
|
1597
|
-
id: randomUUID(),
|
|
1598
|
-
})));
|
|
1646
|
+
const requests = [...(this.activeSubagentRequests.get(parentPiboSessionId) ?? [])];
|
|
1647
|
+
for (const request of requests)
|
|
1648
|
+
request.abortController.abort();
|
|
1649
|
+
const settlements = await Promise.all(requests.map(async (request) => await request.settled));
|
|
1650
|
+
const failures = settlements.flatMap((settlement) => settlement.status === "rejected" ? [settlement.reason] : []);
|
|
1651
|
+
if (failures.length > 0)
|
|
1652
|
+
throw new AggregateError(failures, "Failed to cancel active subagent requests.");
|
|
1599
1653
|
}
|
|
1600
1654
|
createAgentsController(parentPiboSessionId) {
|
|
1601
1655
|
return {
|
|
1602
|
-
sendMessage: async ({ subagent, message, threadKey, toolCallId, signal }) => {
|
|
1656
|
+
sendMessage: async ({ subagent, message, threadKey, toolCallId, requestId, parentProvenance, signal }) => {
|
|
1657
|
+
if (signal?.aborted)
|
|
1658
|
+
throw subagentAbortError();
|
|
1659
|
+
if (typeof requestId !== "string" || !requestId.trim())
|
|
1660
|
+
throw new Error("Delegated agent requestId is required.");
|
|
1603
1661
|
this.assertSubagentDepth(parentPiboSessionId, subagent);
|
|
1604
1662
|
const child = this.resolveSubagentSession(parentPiboSessionId, subagent, threadKey);
|
|
1605
1663
|
const resolvedThreadKey = typeof child.metadata?.threadKey === "string" ? child.metadata.threadKey : "";
|
|
1664
|
+
const loopJobId = parentProvenance?.kind === "loop-run"
|
|
1665
|
+
? parentProvenance.jobId
|
|
1666
|
+
: parentProvenance?.kind === "subagent-request"
|
|
1667
|
+
? parentProvenance.loopJobId
|
|
1668
|
+
: undefined;
|
|
1669
|
+
const loopRunId = parentProvenance?.kind === "loop-run"
|
|
1670
|
+
? parentProvenance.runId
|
|
1671
|
+
: parentProvenance?.kind === "subagent-request"
|
|
1672
|
+
? parentProvenance.loopRunId
|
|
1673
|
+
: undefined;
|
|
1606
1674
|
const event = {
|
|
1607
1675
|
type: "message",
|
|
1608
1676
|
piboSessionId: child.id,
|
|
1609
1677
|
text: message,
|
|
1610
1678
|
source: "actor",
|
|
1611
1679
|
id: randomUUID(),
|
|
1680
|
+
provenance: {
|
|
1681
|
+
kind: "subagent-request",
|
|
1682
|
+
requestId,
|
|
1683
|
+
controllerPiboSessionId: parentPiboSessionId,
|
|
1684
|
+
...(loopJobId ? { loopJobId } : {}),
|
|
1685
|
+
...(loopRunId ? { loopRunId } : {}),
|
|
1686
|
+
},
|
|
1612
1687
|
};
|
|
1688
|
+
this.subagentRequestIdsByEvent.set(subagentRequestEventKey(child.id, event.id), requestId);
|
|
1613
1689
|
this.emitOutput({
|
|
1614
1690
|
type: "subagent_session",
|
|
1615
1691
|
piboSessionId: parentPiboSessionId,
|
|
1692
|
+
requestId,
|
|
1616
1693
|
toolCallId,
|
|
1617
1694
|
toolName: "pibo_agents_send_message",
|
|
1618
1695
|
subagentName: subagent.name,
|
|
1619
1696
|
childPiboSessionId: child.id,
|
|
1620
1697
|
threadKey: resolvedThreadKey,
|
|
1621
1698
|
});
|
|
1622
|
-
const
|
|
1699
|
+
const parentAbortController = new AbortController();
|
|
1700
|
+
const requestSignal = signal
|
|
1701
|
+
? AbortSignal.any([signal, parentAbortController.signal])
|
|
1702
|
+
: parentAbortController.signal;
|
|
1703
|
+
let resolveSettled;
|
|
1704
|
+
const settled = new Promise((resolve) => {
|
|
1705
|
+
resolveSettled = resolve;
|
|
1706
|
+
});
|
|
1707
|
+
const untrack = this.trackActiveSubagent(parentPiboSessionId, {
|
|
1708
|
+
abortController: parentAbortController,
|
|
1709
|
+
settled,
|
|
1710
|
+
});
|
|
1711
|
+
let settlement = { status: "fulfilled" };
|
|
1623
1712
|
try {
|
|
1624
|
-
const reply = await this.emitMessageAndWaitForReply(event,
|
|
1713
|
+
const reply = await this.emitMessageAndWaitForReply(event, undefined, requestSignal);
|
|
1625
1714
|
return {
|
|
1715
|
+
requestId,
|
|
1626
1716
|
agentId: child.id,
|
|
1627
1717
|
name: subagent.name,
|
|
1628
1718
|
profile: child.profile,
|
|
1629
1719
|
threadKey: resolvedThreadKey,
|
|
1630
1720
|
eventId: event.id,
|
|
1721
|
+
finalMessage: reply.text,
|
|
1631
1722
|
reply,
|
|
1632
1723
|
};
|
|
1633
1724
|
}
|
|
1725
|
+
catch (error) {
|
|
1726
|
+
const confirmedParentCancellation = parentAbortController.signal.aborted
|
|
1727
|
+
&& error instanceof Error
|
|
1728
|
+
&& error.name === "AbortError";
|
|
1729
|
+
if (!confirmedParentCancellation)
|
|
1730
|
+
settlement = { status: "rejected", reason: error };
|
|
1731
|
+
throw error;
|
|
1732
|
+
}
|
|
1634
1733
|
finally {
|
|
1734
|
+
this.subagentRequestIdsByEvent.delete(subagentRequestEventKey(child.id, event.id));
|
|
1635
1735
|
untrack();
|
|
1736
|
+
resolveSettled?.(settlement);
|
|
1636
1737
|
}
|
|
1637
1738
|
},
|
|
1638
1739
|
listAgents: () => this.listManagedAgents(parentPiboSessionId),
|
|
@@ -1680,6 +1781,7 @@ export class PiboSessionRouter {
|
|
|
1680
1781
|
if (since !== undefined && until !== undefined && since > until) {
|
|
1681
1782
|
throw new Error("Agent observation since must not be after until.");
|
|
1682
1783
|
}
|
|
1784
|
+
const requestIds = input.requestIds ? new Set(input.requestIds) : undefined;
|
|
1683
1785
|
const agentIds = input.agentIds ? new Set(input.agentIds) : undefined;
|
|
1684
1786
|
if (agentIds) {
|
|
1685
1787
|
for (const agentId of agentIds)
|
|
@@ -1689,10 +1791,13 @@ export class PiboSessionRouter {
|
|
|
1689
1791
|
const threadKeys = input.threadKeys ? new Set(input.threadKeys) : undefined;
|
|
1690
1792
|
const eventTypes = input.eventTypes ? new Set(input.eventTypes) : undefined;
|
|
1691
1793
|
const kinds = input.kinds ? new Set(input.kinds) : undefined;
|
|
1794
|
+
const roles = input.roles ? new Set(input.roles) : undefined;
|
|
1692
1795
|
const textContains = input.textContains?.toLowerCase();
|
|
1693
1796
|
const matches = this.agentObservations.filter((observation) => {
|
|
1694
1797
|
if (observation.managingParentId !== parentPiboSessionId)
|
|
1695
1798
|
return false;
|
|
1799
|
+
if (requestIds && (!observation.requestId || !requestIds.has(observation.requestId)))
|
|
1800
|
+
return false;
|
|
1696
1801
|
if (agentIds && !agentIds.has(observation.agentId))
|
|
1697
1802
|
return false;
|
|
1698
1803
|
if (names && !names.has(observation.name))
|
|
@@ -1703,6 +1808,8 @@ export class PiboSessionRouter {
|
|
|
1703
1808
|
return false;
|
|
1704
1809
|
if (kinds && !kinds.has(observation.kind))
|
|
1705
1810
|
return false;
|
|
1811
|
+
if (roles && (!observation.role || !roles.has(observation.role)))
|
|
1812
|
+
return false;
|
|
1706
1813
|
if (afterSequence !== undefined && observation.sequence <= afterSequence)
|
|
1707
1814
|
return false;
|
|
1708
1815
|
const createdAt = Date.parse(observation.createdAt);
|
|
@@ -1744,9 +1851,8 @@ export class PiboSessionRouter {
|
|
|
1744
1851
|
const child = this.requireManagedAgent(parentPiboSessionId, agentId);
|
|
1745
1852
|
const ids = [agentId, ...this.descendantSessionIds(agentId)];
|
|
1746
1853
|
const idSet = new Set(ids);
|
|
1747
|
-
const
|
|
1748
|
-
.filter((run) => idSet.has(run.controllerPiboSessionId) && !isTerminalRunStatus(run.status))
|
|
1749
|
-
.map((run) => run.runId);
|
|
1854
|
+
const cancellableRuns = this.runRegistry.listAll({ includeConsumed: true, includeDetached: true })
|
|
1855
|
+
.filter((run) => idSet.has(run.controllerPiboSessionId) && !isTerminalRunStatus(run.status));
|
|
1750
1856
|
await Promise.allSettled(ids.flatMap((id) => this.sessions.has(id)
|
|
1751
1857
|
? [this.emit({ type: "execution", piboSessionId: id, action: "abort", id: randomUUID() })]
|
|
1752
1858
|
: []));
|
|
@@ -1760,19 +1866,42 @@ export class PiboSessionRouter {
|
|
|
1760
1866
|
});
|
|
1761
1867
|
}
|
|
1762
1868
|
await this.disposeSessionSubtree(agentId, `killed by parent ${parentPiboSessionId}`, { cancelRuns: true });
|
|
1869
|
+
const cancelledRuns = cancellableRuns.flatMap((run) => {
|
|
1870
|
+
const current = this.runRegistry.status(run.controllerPiboSessionId, run.runId);
|
|
1871
|
+
return current.status === "cancelled" ? [run.runId] : [];
|
|
1872
|
+
});
|
|
1763
1873
|
return { agentId, killed: ids, cancelledRuns };
|
|
1764
1874
|
}
|
|
1765
|
-
async
|
|
1766
|
-
const
|
|
1767
|
-
|
|
1768
|
-
|
|
1875
|
+
async invokeRunCancellationHandler(run) {
|
|
1876
|
+
const cancel = this.runCancellationHandlers.get(run.runId);
|
|
1877
|
+
if (!cancel) {
|
|
1878
|
+
const current = this.runRegistry.status(run.controllerPiboSessionId, run.runId);
|
|
1879
|
+
if (isTerminalRunStatus(current.status) || !this.activeRunExecutions.has(run.runId))
|
|
1769
1880
|
return;
|
|
1881
|
+
throw new PiboRunCancellationError(`Yielded run "${run.runId}" has active execution but does not expose a cancellation handler.`);
|
|
1882
|
+
}
|
|
1883
|
+
await cancel();
|
|
1884
|
+
if (this.runCancellationHandlers.get(run.runId) === cancel)
|
|
1770
1885
|
this.runCancellationHandlers.delete(run.runId);
|
|
1771
|
-
|
|
1886
|
+
}
|
|
1887
|
+
async invokeRunCancellationHandlers(runs) {
|
|
1888
|
+
const results = await Promise.allSettled(runs.map((run) => this.invokeRunCancellationHandler(run)));
|
|
1889
|
+
const failures = results.flatMap((result) => result.status === "rejected" ? [result.reason] : []);
|
|
1890
|
+
if (failures.length > 0)
|
|
1891
|
+
throw new AggregateError(failures, "Failed to terminate yielded runs.");
|
|
1892
|
+
}
|
|
1893
|
+
async cancelRunsAfterSettlement(runs, reason) {
|
|
1894
|
+
const results = await Promise.allSettled(runs.map(async (run) => {
|
|
1895
|
+
await this.invokeRunCancellationHandler(run);
|
|
1896
|
+
const current = this.runRegistry.status(run.controllerPiboSessionId, run.runId);
|
|
1897
|
+
return isTerminalRunStatus(current.status)
|
|
1898
|
+
? current
|
|
1899
|
+
: this.runRegistry.cancel(run.controllerPiboSessionId, run.runId, reason);
|
|
1772
1900
|
}));
|
|
1773
1901
|
const failures = results.flatMap((result) => result.status === "rejected" ? [result.reason] : []);
|
|
1774
1902
|
if (failures.length > 0)
|
|
1775
|
-
throw new AggregateError(failures, "Failed to terminate
|
|
1903
|
+
throw new AggregateError(failures, "Failed to terminate yielded runs before cancellation settlement.");
|
|
1904
|
+
return results.flatMap((result) => result.status === "fulfilled" && result.value.status === "cancelled" ? [result.value] : []);
|
|
1776
1905
|
}
|
|
1777
1906
|
createRunToolController(parentPiboSessionId) {
|
|
1778
1907
|
return {
|
|
@@ -1796,17 +1925,44 @@ export class PiboSessionRouter {
|
|
|
1796
1925
|
timeoutMs,
|
|
1797
1926
|
serviceWarning,
|
|
1798
1927
|
resources,
|
|
1928
|
+
origin: yieldedRunOrigin(this.sessions.get(parentPiboSessionId)?.getActiveMessage?.()),
|
|
1799
1929
|
});
|
|
1800
1930
|
}
|
|
1801
1931
|
catch (error) {
|
|
1802
1932
|
admission.release();
|
|
1803
1933
|
throw error;
|
|
1804
1934
|
}
|
|
1805
|
-
|
|
1806
|
-
|
|
1935
|
+
this.activeRunExecutions.add(run.runId);
|
|
1936
|
+
const cancellation = { state: "none" };
|
|
1937
|
+
let resolveRunTaskSettled;
|
|
1938
|
+
const runTaskSettled = new Promise((resolve) => { resolveRunTaskSettled = resolve; });
|
|
1939
|
+
if (cancel) {
|
|
1940
|
+
let cancellationAttempt;
|
|
1941
|
+
this.runCancellationHandlers.set(run.runId, () => {
|
|
1942
|
+
cancellationAttempt ??= (async () => {
|
|
1943
|
+
cancellation.state = "pending";
|
|
1944
|
+
let resolveDecision;
|
|
1945
|
+
cancellation.decision = new Promise((resolve) => { resolveDecision = resolve; });
|
|
1946
|
+
try {
|
|
1947
|
+
await waitForRunCancellationSettlement(Promise.resolve().then(cancel));
|
|
1948
|
+
cancellation.state = "confirmed";
|
|
1949
|
+
resolveDecision?.();
|
|
1950
|
+
await waitForRunCancellationSettlement(runTaskSettled);
|
|
1951
|
+
}
|
|
1952
|
+
catch (error) {
|
|
1953
|
+
cancellation.state = "failed";
|
|
1954
|
+
throw error;
|
|
1955
|
+
}
|
|
1956
|
+
finally {
|
|
1957
|
+
resolveDecision?.();
|
|
1958
|
+
}
|
|
1959
|
+
})();
|
|
1960
|
+
return cancellationAttempt;
|
|
1961
|
+
});
|
|
1962
|
+
}
|
|
1807
1963
|
void (async () => {
|
|
1808
1964
|
try {
|
|
1809
|
-
const result = await execute();
|
|
1965
|
+
const result = await execute(run.runId);
|
|
1810
1966
|
if (resources)
|
|
1811
1967
|
this.runRegistry.updateResources(run.runId, resources);
|
|
1812
1968
|
const completed = this.runRegistry.complete(run.runId, result);
|
|
@@ -1814,6 +1970,12 @@ export class PiboSessionRouter {
|
|
|
1814
1970
|
this.handleTerminalRunReminder(parentPiboSessionId, completed.runId, reminderGeneration);
|
|
1815
1971
|
}
|
|
1816
1972
|
catch (error) {
|
|
1973
|
+
if (error instanceof PiboRunCancelledError) {
|
|
1974
|
+
if (cancellation.state === "pending")
|
|
1975
|
+
await cancellation.decision;
|
|
1976
|
+
if (cancellation.state === "confirmed")
|
|
1977
|
+
return;
|
|
1978
|
+
}
|
|
1817
1979
|
const message = error instanceof Error ? error.message : String(error);
|
|
1818
1980
|
if (resources)
|
|
1819
1981
|
this.runRegistry.updateResources(run.runId, resources);
|
|
@@ -1827,7 +1989,9 @@ export class PiboSessionRouter {
|
|
|
1827
1989
|
}
|
|
1828
1990
|
finally {
|
|
1829
1991
|
this.runCancellationHandlers.delete(run.runId);
|
|
1992
|
+
this.activeRunExecutions.delete(run.runId);
|
|
1830
1993
|
admission.release();
|
|
1994
|
+
resolveRunTaskSettled?.();
|
|
1831
1995
|
}
|
|
1832
1996
|
})();
|
|
1833
1997
|
return run;
|
|
@@ -1842,14 +2006,15 @@ export class PiboSessionRouter {
|
|
|
1842
2006
|
return run;
|
|
1843
2007
|
},
|
|
1844
2008
|
cancelRun: async (runId) => {
|
|
1845
|
-
const
|
|
2009
|
+
const current = this.runRegistry.status(parentPiboSessionId, runId);
|
|
1846
2010
|
try {
|
|
1847
|
-
|
|
2011
|
+
if (!isTerminalRunStatus(current.status))
|
|
2012
|
+
await this.invokeRunCancellationHandlers([current]);
|
|
2013
|
+
return this.runRegistry.cancel(parentPiboSessionId, runId);
|
|
1848
2014
|
}
|
|
1849
2015
|
finally {
|
|
1850
2016
|
this.refreshQueuedRunReminders(parentPiboSessionId);
|
|
1851
2017
|
}
|
|
1852
|
-
return cancelled;
|
|
1853
2018
|
},
|
|
1854
2019
|
ackRun: (runId) => {
|
|
1855
2020
|
const run = this.runRegistry.ack(parentPiboSessionId, runId);
|
|
@@ -1944,10 +2109,18 @@ export class PiboSessionRouter {
|
|
|
1944
2109
|
const source = piboAgentObservationSourceFromEvent(event);
|
|
1945
2110
|
const role = piboAgentObservationRole(source);
|
|
1946
2111
|
const text = piboAgentObservationText(source);
|
|
2112
|
+
const provenance = "provenance" in event ? event.provenance : undefined;
|
|
2113
|
+
const eventId = "eventId" in event && typeof event.eventId === "string" ? event.eventId : undefined;
|
|
2114
|
+
const requestId = provenance?.kind === "subagent-request"
|
|
2115
|
+
? provenance.requestId
|
|
2116
|
+
: eventId
|
|
2117
|
+
? this.subagentRequestIdsByEvent.get(subagentRequestEventKey(session.id, eventId))
|
|
2118
|
+
: undefined;
|
|
1947
2119
|
const observation = {
|
|
1948
2120
|
managingParentId: session.parentId,
|
|
1949
2121
|
sequence: this.nextAgentObservationSequence++,
|
|
1950
2122
|
createdAt: new Date().toISOString(),
|
|
2123
|
+
...(requestId ? { requestId } : {}),
|
|
1951
2124
|
agentId: session.id,
|
|
1952
2125
|
name,
|
|
1953
2126
|
...(typeof session.metadata?.threadKey === "string" ? { threadKey: session.metadata.threadKey } : {}),
|
|
@@ -2067,8 +2240,8 @@ export class PiboSessionRouter {
|
|
|
2067
2240
|
piboSessionId,
|
|
2068
2241
|
text: formatRunReminderMessage(notification),
|
|
2069
2242
|
source: "service",
|
|
2070
|
-
capabilityScope: "run-reminder",
|
|
2071
2243
|
id: randomUUID(),
|
|
2244
|
+
provenance: runReminderProvenance(notification),
|
|
2072
2245
|
});
|
|
2073
2246
|
}
|
|
2074
2247
|
catch (error) {
|