@pasko70/pibo 2.4.2 → 2.5.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/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 +30 -23
- 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 +42 -27
- 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-CrDtveZB.js → dist-BxJuOpXP.js} +1 -1
- package/dist/apps/chat-ui/assets/{dist-3YG57JXi.js → dist-CvA-WTQT.js} +1 -1
- package/dist/apps/chat-ui/assets/{dist-Cw9po47P.js → dist-DPzsIE8h.js} +1 -1
- package/dist/apps/chat-ui/assets/{dist-DTRjeLwO.js → dist-Dj5P89Wy.js} +1 -1
- package/dist/apps/chat-ui/assets/{dist-BeqHbnGN.js → dist-Sll26U24.js} +1 -1
- package/dist/apps/chat-ui/assets/index-0WZI2phJ.css +1 -0
- package/dist/apps/chat-ui/assets/index-BW5XFgYP.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.2.vsix → pibo-vscode-ext-2.5.0.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 +152 -69
- package/dist/gateway/server.js +1 -0
- package/dist/loops/accounting.js +80 -0
- package/dist/loops/service.js +51 -16
- package/dist/loops/store.js +50 -14
- package/dist/runs/lifecycle.js +26 -1
- package/dist/runs/registry.js +29 -47
- package/dist/runs/tools.js +23 -22
- 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 { PiboRunCancellationError, PiboRunCancelledError, 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 = {
|
|
@@ -258,12 +260,14 @@ export class PiboSessionRouter {
|
|
|
258
260
|
runtimeResourceSessions = new Map();
|
|
259
261
|
runtimeAuthFingerprints = new Map();
|
|
260
262
|
activeSubagentRequests = new Map();
|
|
263
|
+
subagentRequestIdsByEvent = new Map();
|
|
261
264
|
agentObservations = [];
|
|
262
265
|
agentObservationEvictedThroughByParent = new Map();
|
|
263
266
|
nextAgentObservationSequence = 1;
|
|
264
267
|
scheduledRunReminders = new Map();
|
|
265
268
|
runReminderGenerations = new Map();
|
|
266
269
|
runCancellationHandlers = new Map();
|
|
270
|
+
activeRunExecutions = new Set();
|
|
267
271
|
quiescingSessions = new Set();
|
|
268
272
|
disposingSessions = new Map();
|
|
269
273
|
idleSessionTimers = new Map();
|
|
@@ -481,24 +485,24 @@ export class PiboSessionRouter {
|
|
|
481
485
|
const failures = [];
|
|
482
486
|
for (const id of ids) {
|
|
483
487
|
const session = this.sessions.get(id);
|
|
484
|
-
if (session)
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
catch (error) {
|
|
490
|
-
failures.push(error);
|
|
491
|
-
}
|
|
488
|
+
if (!session)
|
|
489
|
+
continue;
|
|
490
|
+
this.signalRegistry.project({ type: "session_disposed", piboSessionId: id, reason: "kill" });
|
|
491
|
+
try {
|
|
492
|
+
killed.push(await session.kill());
|
|
492
493
|
}
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
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);
|
|
502
506
|
}
|
|
503
507
|
}
|
|
504
508
|
try {
|
|
@@ -555,10 +559,9 @@ export class PiboSessionRouter {
|
|
|
555
559
|
const operation = (async () => {
|
|
556
560
|
await startGate;
|
|
557
561
|
const runCancellationResults = options.cancelRuns
|
|
558
|
-
? await Promise.allSettled(
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
}))
|
|
562
|
+
? await Promise.allSettled([
|
|
563
|
+
this.cancelRunsAfterSettlement(ids.flatMap((id) => this.runRegistry.listActiveControllerRuns(id)), reason),
|
|
564
|
+
])
|
|
562
565
|
: [];
|
|
563
566
|
const pending = ids.map((id) => this.pendingSessions.get(id)).filter((value) => Boolean(value));
|
|
564
567
|
if (pending.length > 0)
|
|
@@ -632,11 +635,11 @@ export class PiboSessionRouter {
|
|
|
632
635
|
const childSession = this.sessions.get(id);
|
|
633
636
|
if (childSession)
|
|
634
637
|
killed.push(await childSession.kill());
|
|
635
|
-
if (options?.includeRuns)
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
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));
|
|
640
643
|
}
|
|
641
644
|
return { killed, cancelledRuns };
|
|
642
645
|
}
|
|
@@ -687,6 +690,18 @@ export class PiboSessionRouter {
|
|
|
687
690
|
const session = this.sessionStore.get(piboSessionId);
|
|
688
691
|
return session ? structuredClone(this.resolveSessionRuntimeBinding(session)) : undefined;
|
|
689
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
|
+
}
|
|
690
705
|
async rebindSessionRuntime(piboSessionId, input) {
|
|
691
706
|
if (this.quiescingSessions.has(piboSessionId)) {
|
|
692
707
|
throw new Error(`Pibo session "${piboSessionId}" is already quiescing.`);
|
|
@@ -868,7 +883,7 @@ export class PiboSessionRouter {
|
|
|
868
883
|
subscribeSignalStatuses(listener) {
|
|
869
884
|
return this.signalRegistry.subscribeAll(listener);
|
|
870
885
|
}
|
|
871
|
-
async emitMessageAndWaitForReply(event, timeoutMs
|
|
886
|
+
async emitMessageAndWaitForReply(event, timeoutMs, signal) {
|
|
872
887
|
const eventWithId = { ...event, id: event.id ?? randomUUID() };
|
|
873
888
|
return await new Promise((resolve, reject) => {
|
|
874
889
|
let settled = false;
|
|
@@ -932,9 +947,11 @@ export class PiboSessionRouter {
|
|
|
932
947
|
return;
|
|
933
948
|
}
|
|
934
949
|
signal?.addEventListener("abort", onAbort, { once: true });
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
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
|
+
}
|
|
938
955
|
dispatchPromise = this.emit(eventWithId);
|
|
939
956
|
dispatchPromise.catch((error) => {
|
|
940
957
|
finish(error instanceof Error ? error : new Error(String(error)));
|
|
@@ -963,8 +980,9 @@ export class PiboSessionRouter {
|
|
|
963
980
|
for (const timer of this.idleSessionTimers.values())
|
|
964
981
|
clearTimeout(timer);
|
|
965
982
|
this.idleSessionTimers.clear();
|
|
966
|
-
const
|
|
967
|
-
|
|
983
|
+
const runCancellationResult = await Promise.allSettled([
|
|
984
|
+
this.cancelRunsAfterSettlement(this.runRegistry.listActiveRuns(), "Pibo session router was disposed."),
|
|
985
|
+
]);
|
|
968
986
|
this.scheduledRunReminders.clear();
|
|
969
987
|
const closeResult = await Promise.allSettled([this.runtimeRegistry.closeAll({ force: true })]);
|
|
970
988
|
const disposeResults = await Promise.allSettled(sessions.map(([id, session]) => this.disposeRoutedSession(id, session, "router disposed")));
|
|
@@ -994,6 +1012,7 @@ export class PiboSessionRouter {
|
|
|
994
1012
|
await this.runtimeResourceService.dispose();
|
|
995
1013
|
this.runtimeResourceSessions.clear();
|
|
996
1014
|
this.activeSubagentRequests.clear();
|
|
1015
|
+
this.subagentRequestIdsByEvent.clear();
|
|
997
1016
|
this.agentObservations.length = 0;
|
|
998
1017
|
this.agentObservationEvictedThroughByParent.clear();
|
|
999
1018
|
await this.telemetryWriter?.dispose();
|
|
@@ -1078,19 +1097,13 @@ export class PiboSessionRouter {
|
|
|
1078
1097
|
const piboSession = this.resolvePiboSession(piboSessionId);
|
|
1079
1098
|
let session;
|
|
1080
1099
|
this.signalRegistry.project({ type: "session_created", session: piboSession });
|
|
1081
|
-
const profile = createPiboProfileFromRegistryOrDefault(this.pluginRegistry, piboSession.profile);
|
|
1082
1100
|
let binding = this.resolveSessionRuntimeBinding(piboSession);
|
|
1083
1101
|
const parent = piboSession.parentId ? this.resolvePiboSession(piboSession.parentId) : undefined;
|
|
1084
1102
|
const parentBinding = parent ? this.resolveSessionRuntimeBinding(parent) : undefined;
|
|
1085
1103
|
const parentModelScopeId = parent ? parentBinding?.nativeSessionId ?? parent.id : undefined;
|
|
1086
|
-
const runtimeParentNativeSessionId = parentBinding
|
|
1087
|
-
&& parentBinding.runtimeInstanceId === binding.runtimeInstanceId
|
|
1088
|
-
&& parentBinding.adapterId === binding.adapterId
|
|
1089
|
-
? parentBinding.nativeSessionId
|
|
1090
|
-
: undefined;
|
|
1091
1104
|
const modelDefaults = this.resolveModelDefaults();
|
|
1092
1105
|
const initialThinkingLevel = resolvePiboSessionInitialThinkingLevel(piboSession);
|
|
1093
|
-
const sessionProfile =
|
|
1106
|
+
const sessionProfile = this.getSessionRuntimeProfile(piboSession.id);
|
|
1094
1107
|
const persistedHistoryHandoff = readPortableHistoryHandoffMetadata(binding.metadata);
|
|
1095
1108
|
if (binding.metadata?.[PORTABLE_HISTORY_HANDOFF_METADATA_KEY] !== undefined && !persistedHistoryHandoff) {
|
|
1096
1109
|
throw new Error("The pending portable history handoff metadata is invalid; refusing to start a contextless target runtime.");
|
|
@@ -1640,22 +1653,43 @@ export class PiboSessionRouter {
|
|
|
1640
1653
|
}
|
|
1641
1654
|
createAgentsController(parentPiboSessionId) {
|
|
1642
1655
|
return {
|
|
1643
|
-
sendMessage: async ({ subagent, message, threadKey, toolCallId, signal }) => {
|
|
1656
|
+
sendMessage: async ({ subagent, message, threadKey, toolCallId, requestId, parentProvenance, signal }) => {
|
|
1644
1657
|
if (signal?.aborted)
|
|
1645
1658
|
throw subagentAbortError();
|
|
1659
|
+
if (typeof requestId !== "string" || !requestId.trim())
|
|
1660
|
+
throw new Error("Delegated agent requestId is required.");
|
|
1646
1661
|
this.assertSubagentDepth(parentPiboSessionId, subagent);
|
|
1647
1662
|
const child = this.resolveSubagentSession(parentPiboSessionId, subagent, threadKey);
|
|
1648
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;
|
|
1649
1674
|
const event = {
|
|
1650
1675
|
type: "message",
|
|
1651
1676
|
piboSessionId: child.id,
|
|
1652
1677
|
text: message,
|
|
1653
1678
|
source: "actor",
|
|
1654
1679
|
id: randomUUID(),
|
|
1680
|
+
provenance: {
|
|
1681
|
+
kind: "subagent-request",
|
|
1682
|
+
requestId,
|
|
1683
|
+
controllerPiboSessionId: parentPiboSessionId,
|
|
1684
|
+
...(loopJobId ? { loopJobId } : {}),
|
|
1685
|
+
...(loopRunId ? { loopRunId } : {}),
|
|
1686
|
+
},
|
|
1655
1687
|
};
|
|
1688
|
+
this.subagentRequestIdsByEvent.set(subagentRequestEventKey(child.id, event.id), requestId);
|
|
1656
1689
|
this.emitOutput({
|
|
1657
1690
|
type: "subagent_session",
|
|
1658
1691
|
piboSessionId: parentPiboSessionId,
|
|
1692
|
+
requestId,
|
|
1659
1693
|
toolCallId,
|
|
1660
1694
|
toolName: "pibo_agents_send_message",
|
|
1661
1695
|
subagentName: subagent.name,
|
|
@@ -1676,13 +1710,15 @@ export class PiboSessionRouter {
|
|
|
1676
1710
|
});
|
|
1677
1711
|
let settlement = { status: "fulfilled" };
|
|
1678
1712
|
try {
|
|
1679
|
-
const reply = await this.emitMessageAndWaitForReply(event,
|
|
1713
|
+
const reply = await this.emitMessageAndWaitForReply(event, undefined, requestSignal);
|
|
1680
1714
|
return {
|
|
1715
|
+
requestId,
|
|
1681
1716
|
agentId: child.id,
|
|
1682
1717
|
name: subagent.name,
|
|
1683
1718
|
profile: child.profile,
|
|
1684
1719
|
threadKey: resolvedThreadKey,
|
|
1685
1720
|
eventId: event.id,
|
|
1721
|
+
finalMessage: reply.text,
|
|
1686
1722
|
reply,
|
|
1687
1723
|
};
|
|
1688
1724
|
}
|
|
@@ -1695,6 +1731,7 @@ export class PiboSessionRouter {
|
|
|
1695
1731
|
throw error;
|
|
1696
1732
|
}
|
|
1697
1733
|
finally {
|
|
1734
|
+
this.subagentRequestIdsByEvent.delete(subagentRequestEventKey(child.id, event.id));
|
|
1698
1735
|
untrack();
|
|
1699
1736
|
resolveSettled?.(settlement);
|
|
1700
1737
|
}
|
|
@@ -1744,6 +1781,7 @@ export class PiboSessionRouter {
|
|
|
1744
1781
|
if (since !== undefined && until !== undefined && since > until) {
|
|
1745
1782
|
throw new Error("Agent observation since must not be after until.");
|
|
1746
1783
|
}
|
|
1784
|
+
const requestIds = input.requestIds ? new Set(input.requestIds) : undefined;
|
|
1747
1785
|
const agentIds = input.agentIds ? new Set(input.agentIds) : undefined;
|
|
1748
1786
|
if (agentIds) {
|
|
1749
1787
|
for (const agentId of agentIds)
|
|
@@ -1753,10 +1791,13 @@ export class PiboSessionRouter {
|
|
|
1753
1791
|
const threadKeys = input.threadKeys ? new Set(input.threadKeys) : undefined;
|
|
1754
1792
|
const eventTypes = input.eventTypes ? new Set(input.eventTypes) : undefined;
|
|
1755
1793
|
const kinds = input.kinds ? new Set(input.kinds) : undefined;
|
|
1794
|
+
const roles = input.roles ? new Set(input.roles) : undefined;
|
|
1756
1795
|
const textContains = input.textContains?.toLowerCase();
|
|
1757
1796
|
const matches = this.agentObservations.filter((observation) => {
|
|
1758
1797
|
if (observation.managingParentId !== parentPiboSessionId)
|
|
1759
1798
|
return false;
|
|
1799
|
+
if (requestIds && (!observation.requestId || !requestIds.has(observation.requestId)))
|
|
1800
|
+
return false;
|
|
1760
1801
|
if (agentIds && !agentIds.has(observation.agentId))
|
|
1761
1802
|
return false;
|
|
1762
1803
|
if (names && !names.has(observation.name))
|
|
@@ -1767,6 +1808,8 @@ export class PiboSessionRouter {
|
|
|
1767
1808
|
return false;
|
|
1768
1809
|
if (kinds && !kinds.has(observation.kind))
|
|
1769
1810
|
return false;
|
|
1811
|
+
if (roles && (!observation.role || !roles.has(observation.role)))
|
|
1812
|
+
return false;
|
|
1770
1813
|
if (afterSequence !== undefined && observation.sequence <= afterSequence)
|
|
1771
1814
|
return false;
|
|
1772
1815
|
const createdAt = Date.parse(observation.createdAt);
|
|
@@ -1808,9 +1851,8 @@ export class PiboSessionRouter {
|
|
|
1808
1851
|
const child = this.requireManagedAgent(parentPiboSessionId, agentId);
|
|
1809
1852
|
const ids = [agentId, ...this.descendantSessionIds(agentId)];
|
|
1810
1853
|
const idSet = new Set(ids);
|
|
1811
|
-
const
|
|
1812
|
-
.filter((run) => idSet.has(run.controllerPiboSessionId) && !isTerminalRunStatus(run.status))
|
|
1813
|
-
.map((run) => run.runId);
|
|
1854
|
+
const cancellableRuns = this.runRegistry.listAll({ includeConsumed: true, includeDetached: true })
|
|
1855
|
+
.filter((run) => idSet.has(run.controllerPiboSessionId) && !isTerminalRunStatus(run.status));
|
|
1814
1856
|
await Promise.allSettled(ids.flatMap((id) => this.sessions.has(id)
|
|
1815
1857
|
? [this.emit({ type: "execution", piboSessionId: id, action: "abort", id: randomUUID() })]
|
|
1816
1858
|
: []));
|
|
@@ -1824,20 +1866,42 @@ export class PiboSessionRouter {
|
|
|
1824
1866
|
});
|
|
1825
1867
|
}
|
|
1826
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
|
+
});
|
|
1827
1873
|
return { agentId, killed: ids, cancelledRuns };
|
|
1828
1874
|
}
|
|
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))
|
|
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)
|
|
1885
|
+
this.runCancellationHandlers.delete(run.runId);
|
|
1886
|
+
}
|
|
1829
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) {
|
|
1830
1894
|
const results = await Promise.allSettled(runs.map(async (run) => {
|
|
1831
|
-
|
|
1832
|
-
|
|
1833
|
-
|
|
1834
|
-
|
|
1835
|
-
|
|
1836
|
-
this.runCancellationHandlers.delete(run.runId);
|
|
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);
|
|
1837
1900
|
}));
|
|
1838
1901
|
const failures = results.flatMap((result) => result.status === "rejected" ? [result.reason] : []);
|
|
1839
1902
|
if (failures.length > 0)
|
|
1840
|
-
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] : []);
|
|
1841
1905
|
}
|
|
1842
1906
|
createRunToolController(parentPiboSessionId) {
|
|
1843
1907
|
return {
|
|
@@ -1868,28 +1932,37 @@ export class PiboSessionRouter {
|
|
|
1868
1932
|
admission.release();
|
|
1869
1933
|
throw error;
|
|
1870
1934
|
}
|
|
1935
|
+
this.activeRunExecutions.add(run.runId);
|
|
1871
1936
|
const cancellation = { state: "none" };
|
|
1937
|
+
let resolveRunTaskSettled;
|
|
1938
|
+
const runTaskSettled = new Promise((resolve) => { resolveRunTaskSettled = resolve; });
|
|
1872
1939
|
if (cancel) {
|
|
1873
|
-
|
|
1874
|
-
|
|
1875
|
-
|
|
1876
|
-
|
|
1877
|
-
|
|
1878
|
-
|
|
1879
|
-
|
|
1880
|
-
|
|
1881
|
-
|
|
1882
|
-
|
|
1883
|
-
|
|
1884
|
-
|
|
1885
|
-
|
|
1886
|
-
|
|
1887
|
-
|
|
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;
|
|
1888
1961
|
});
|
|
1889
1962
|
}
|
|
1890
1963
|
void (async () => {
|
|
1891
1964
|
try {
|
|
1892
|
-
const result = await execute();
|
|
1965
|
+
const result = await execute(run.runId);
|
|
1893
1966
|
if (resources)
|
|
1894
1967
|
this.runRegistry.updateResources(run.runId, resources);
|
|
1895
1968
|
const completed = this.runRegistry.complete(run.runId, result);
|
|
@@ -1916,7 +1989,9 @@ export class PiboSessionRouter {
|
|
|
1916
1989
|
}
|
|
1917
1990
|
finally {
|
|
1918
1991
|
this.runCancellationHandlers.delete(run.runId);
|
|
1992
|
+
this.activeRunExecutions.delete(run.runId);
|
|
1919
1993
|
admission.release();
|
|
1994
|
+
resolveRunTaskSettled?.();
|
|
1920
1995
|
}
|
|
1921
1996
|
})();
|
|
1922
1997
|
return run;
|
|
@@ -2034,10 +2109,18 @@ export class PiboSessionRouter {
|
|
|
2034
2109
|
const source = piboAgentObservationSourceFromEvent(event);
|
|
2035
2110
|
const role = piboAgentObservationRole(source);
|
|
2036
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;
|
|
2037
2119
|
const observation = {
|
|
2038
2120
|
managingParentId: session.parentId,
|
|
2039
2121
|
sequence: this.nextAgentObservationSequence++,
|
|
2040
2122
|
createdAt: new Date().toISOString(),
|
|
2123
|
+
...(requestId ? { requestId } : {}),
|
|
2041
2124
|
agentId: session.id,
|
|
2042
2125
|
name,
|
|
2043
2126
|
...(typeof session.metadata?.threadKey === "string" ? { threadKey: session.metadata.threadKey } : {}),
|
package/dist/gateway/server.js
CHANGED
|
@@ -358,6 +358,7 @@ export class PiboGatewayServer {
|
|
|
358
358
|
findSessions: (input) => this.requireSessionStore().find(input),
|
|
359
359
|
listSessions: () => this.requireSessionStore().list?.() ?? [],
|
|
360
360
|
getSessionRuntimeBinding: (piboSessionId) => this.requireRouter().getSessionRuntimeBinding(piboSessionId),
|
|
361
|
+
getSessionRuntimeProfile: (piboSessionId) => this.requireRouter().getSessionRuntimeProfile(piboSessionId),
|
|
361
362
|
inspectSessionRuntimeHistory: async (piboSessionId) => {
|
|
362
363
|
const session = this.requireSessionStore().get(piboSessionId);
|
|
363
364
|
if (!session)
|
package/dist/loops/accounting.js
CHANGED
|
@@ -1,4 +1,84 @@
|
|
|
1
1
|
export const LOOP_TOKEN_ACCOUNTING_VERSION = 1;
|
|
2
|
+
export function emptyLoopUsageTotals() {
|
|
3
|
+
return {
|
|
4
|
+
inputTokens: 0,
|
|
5
|
+
outputTokens: 0,
|
|
6
|
+
cacheReadTokens: 0,
|
|
7
|
+
cacheWriteTokens: 0,
|
|
8
|
+
reasoningTokens: 0,
|
|
9
|
+
totalTokens: 0,
|
|
10
|
+
costUsd: 0,
|
|
11
|
+
costReportedTurns: 0,
|
|
12
|
+
assistantTurns: 0,
|
|
13
|
+
};
|
|
14
|
+
}
|
|
15
|
+
export function emptyLoopRecursiveUsage() {
|
|
16
|
+
return {
|
|
17
|
+
controller: emptyLoopUsageTotals(),
|
|
18
|
+
descendants: emptyLoopUsageTotals(),
|
|
19
|
+
total: emptyLoopUsageTotals(),
|
|
20
|
+
sessionIds: [],
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
export function assistantUsageTotals(usage) {
|
|
24
|
+
return {
|
|
25
|
+
inputTokens: normalizedTokenCount(usage.inputTokens),
|
|
26
|
+
outputTokens: normalizedTokenCount(usage.outputTokens),
|
|
27
|
+
cacheReadTokens: normalizedTokenCount(usage.cacheReadTokens),
|
|
28
|
+
cacheWriteTokens: normalizedTokenCount(usage.cacheWriteTokens),
|
|
29
|
+
reasoningTokens: normalizedTokenCount(usage.reasoningTokens),
|
|
30
|
+
totalTokens: normalizedTokenCount(usage.totalTokens),
|
|
31
|
+
costUsd: typeof usage.costUsd === 'number' && Number.isFinite(usage.costUsd) ? Math.max(0, usage.costUsd) : 0,
|
|
32
|
+
costReportedTurns: typeof usage.costUsd === 'number' && Number.isFinite(usage.costUsd) ? 1 : 0,
|
|
33
|
+
assistantTurns: 1,
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
function normalizeLoopUsageTotals(value) {
|
|
37
|
+
const costUsd = typeof value?.costUsd === 'number' && Number.isFinite(value.costUsd) ? Math.max(0, value.costUsd) : 0;
|
|
38
|
+
return {
|
|
39
|
+
inputTokens: normalizedTokenCount(value?.inputTokens),
|
|
40
|
+
outputTokens: normalizedTokenCount(value?.outputTokens),
|
|
41
|
+
cacheReadTokens: normalizedTokenCount(value?.cacheReadTokens),
|
|
42
|
+
cacheWriteTokens: normalizedTokenCount(value?.cacheWriteTokens),
|
|
43
|
+
reasoningTokens: normalizedTokenCount(value?.reasoningTokens),
|
|
44
|
+
totalTokens: normalizedTokenCount(value?.totalTokens),
|
|
45
|
+
costUsd,
|
|
46
|
+
costReportedTurns: value?.costReportedTurns === undefined && costUsd > 0 ? 1 : normalizedTokenCount(value?.costReportedTurns),
|
|
47
|
+
assistantTurns: normalizedTokenCount(value?.assistantTurns),
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
function addUsageTotals(left, right) {
|
|
51
|
+
const normalizedLeft = normalizeLoopUsageTotals(left);
|
|
52
|
+
const normalizedRight = normalizeLoopUsageTotals(right);
|
|
53
|
+
return {
|
|
54
|
+
inputTokens: normalizedLeft.inputTokens + normalizedRight.inputTokens,
|
|
55
|
+
outputTokens: normalizedLeft.outputTokens + normalizedRight.outputTokens,
|
|
56
|
+
cacheReadTokens: normalizedLeft.cacheReadTokens + normalizedRight.cacheReadTokens,
|
|
57
|
+
cacheWriteTokens: normalizedLeft.cacheWriteTokens + normalizedRight.cacheWriteTokens,
|
|
58
|
+
reasoningTokens: normalizedLeft.reasoningTokens + normalizedRight.reasoningTokens,
|
|
59
|
+
totalTokens: normalizedLeft.totalTokens + normalizedRight.totalTokens,
|
|
60
|
+
costUsd: normalizedLeft.costUsd + normalizedRight.costUsd,
|
|
61
|
+
costReportedTurns: normalizedLeft.costReportedTurns + normalizedRight.costReportedTurns,
|
|
62
|
+
assistantTurns: normalizedLeft.assistantTurns + normalizedRight.assistantTurns,
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
export function addLoopAssistantUsage(current, usage, input) {
|
|
66
|
+
const base = current
|
|
67
|
+
? {
|
|
68
|
+
controller: normalizeLoopUsageTotals(current.controller),
|
|
69
|
+
descendants: normalizeLoopUsageTotals(current.descendants),
|
|
70
|
+
total: normalizeLoopUsageTotals(current.total),
|
|
71
|
+
sessionIds: Array.isArray(current.sessionIds) ? current.sessionIds.filter((id) => typeof id === 'string') : [],
|
|
72
|
+
}
|
|
73
|
+
: emptyLoopRecursiveUsage();
|
|
74
|
+
const increment = assistantUsageTotals(usage);
|
|
75
|
+
return {
|
|
76
|
+
controller: input.descendant ? { ...base.controller } : addUsageTotals(base.controller, increment),
|
|
77
|
+
descendants: input.descendant ? addUsageTotals(base.descendants, increment) : { ...base.descendants },
|
|
78
|
+
total: addUsageTotals(base.total, increment),
|
|
79
|
+
sessionIds: base.sessionIds.includes(input.piboSessionId) ? [...base.sessionIds] : [...base.sessionIds, input.piboSessionId],
|
|
80
|
+
};
|
|
81
|
+
}
|
|
2
82
|
function normalizedTokenCount(value) {
|
|
3
83
|
return typeof value === 'number' && Number.isFinite(value) ? Math.max(0, Math.floor(value)) : 0;
|
|
4
84
|
}
|
package/dist/loops/service.js
CHANGED
|
@@ -519,30 +519,65 @@ export class PiboLoopService {
|
|
|
519
519
|
getStopConditionDefinitions() { return this.options.context.getLoopStopConditionDefinitions?.() ?? this.options.context.getRalphStopConditionDefinitions?.() ?? createBuiltInLoopStopConditions(); }
|
|
520
520
|
handleOutputEvent(event) {
|
|
521
521
|
const eventId = 'eventId' in event ? event.eventId : undefined;
|
|
522
|
-
if (
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
522
|
+
if (eventId) {
|
|
523
|
+
if (event.type === 'message_queued')
|
|
524
|
+
this.store.updateRunMessageState(eventId, 'queued');
|
|
525
|
+
else if (event.type === 'message_started')
|
|
526
|
+
this.store.updateRunMessageState(eventId, 'active');
|
|
527
|
+
else if (event.type === 'message_finished')
|
|
528
|
+
this.store.updateRunMessageState(eventId, 'finished');
|
|
529
|
+
else if (event.type === 'session_error' && event.errorDetails?.code === 'loop_continuation_invalidated')
|
|
530
|
+
this.store.updateRunMessageState(eventId, 'invalidated');
|
|
531
|
+
}
|
|
532
532
|
if (event.type !== 'assistant_usage')
|
|
533
533
|
return;
|
|
534
|
-
const provenance = event.provenance
|
|
535
|
-
const
|
|
536
|
-
|
|
534
|
+
const provenance = event.provenance;
|
|
535
|
+
const provenanceRunId = provenance?.kind === 'loop-run'
|
|
536
|
+
? provenance.runId
|
|
537
|
+
: provenance?.kind === 'subagent-request'
|
|
538
|
+
? provenance.loopRunId
|
|
539
|
+
: undefined;
|
|
540
|
+
const provenanceJobId = provenance?.kind === 'loop-run'
|
|
541
|
+
? provenance.jobId
|
|
542
|
+
: provenance?.kind === 'subagent-request'
|
|
543
|
+
? provenance.loopJobId
|
|
544
|
+
: undefined;
|
|
545
|
+
const run = provenanceRunId
|
|
546
|
+
? this.store.getRun(provenanceRunId)
|
|
547
|
+
: eventId
|
|
548
|
+
? this.store.getRunByMessageEventId(eventId)
|
|
549
|
+
: undefined;
|
|
550
|
+
if (!run || (provenanceJobId && run.jobId !== provenanceJobId) || !this.isRunSessionOrDescendant(run, event.piboSessionId))
|
|
537
551
|
return;
|
|
538
|
-
if (provenance
|
|
539
|
-
|
|
552
|
+
if (provenance?.kind === 'loop-run'
|
|
553
|
+
&& provenance.cause === 'run-reminder'
|
|
554
|
+
&& run.messageEventId !== provenance.rootEventId)
|
|
540
555
|
return;
|
|
541
556
|
const job = this.store.getJob(run.jobId);
|
|
542
557
|
if (!job || job.mode !== 'goal')
|
|
543
558
|
return;
|
|
544
559
|
const basis = run.accounting?.tokenAccounting?.basis ?? goalTokenAccounting(job).basis;
|
|
545
|
-
this.store.
|
|
560
|
+
this.store.recordGoalAssistantUsage(job.id, run.id, {
|
|
561
|
+
usage: event,
|
|
562
|
+
budgetTokens: goalBudgetTokens(event, basis),
|
|
563
|
+
piboSessionId: event.piboSessionId,
|
|
564
|
+
descendant: event.piboSessionId !== run.piboSessionId,
|
|
565
|
+
});
|
|
566
|
+
}
|
|
567
|
+
isRunSessionOrDescendant(run, piboSessionId) {
|
|
568
|
+
if (!run.piboSessionId)
|
|
569
|
+
return false;
|
|
570
|
+
if (run.piboSessionId === piboSessionId)
|
|
571
|
+
return true;
|
|
572
|
+
let current = this.options.context.getSession(piboSessionId);
|
|
573
|
+
const seen = new Set();
|
|
574
|
+
while (current?.parentId && !seen.has(current.parentId)) {
|
|
575
|
+
if (current.parentId === run.piboSessionId)
|
|
576
|
+
return true;
|
|
577
|
+
seen.add(current.parentId);
|
|
578
|
+
current = this.options.context.getSession(current.parentId);
|
|
579
|
+
}
|
|
580
|
+
return false;
|
|
546
581
|
}
|
|
547
582
|
handleProductEvent(event) {
|
|
548
583
|
if (event.type !== 'pibo.loop.fact' && event.type !== 'loop.fact' && event.type !== 'pibo.ralph.fact' && event.type !== 'ralph.fact')
|