@pasko70/pibo 3.4.1 → 3.4.2
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/routed-session.js +5 -1
- package/dist/agent-runtimes/pi/adapter.js +4 -1
- package/dist/agent-runtimes/pi/history.js +55 -1
- package/dist/apps/chat/data/chat-data-mappers.js +1 -1
- package/dist/apps/chat/stream.js +1 -1
- package/dist/apps/chat/trace-v2.js +1 -0
- package/dist/apps/chat/web-app.js +2 -1
- package/dist/apps/chat-ui/assets/{dist-DnZL1rRp.js → dist-B7Ju7u08.js} +1 -1
- package/dist/apps/chat-ui/assets/{dist-ERKABhp3.js → dist-BDnWOTcs.js} +1 -1
- package/dist/apps/chat-ui/assets/{dist-D0md0xIR.js → dist-CAiO6h6z.js} +1 -1
- package/dist/apps/chat-ui/assets/{dist-Abz605MV.js → dist-CCR1HsaR.js} +1 -1
- package/dist/apps/chat-ui/assets/{dist-DHnaUMlq.js → dist-CXWLhBio.js} +1 -1
- package/dist/apps/chat-ui/assets/index-0cCGS0o3.js +228 -0
- package/dist/apps/chat-ui/assets/index-BMJGoyM8.css +1 -0
- package/dist/apps/chat-ui/index.html +2 -2
- package/dist/apps/chat-vscode-web/assets/index-BD3Ogz9Z.js +43 -0
- package/dist/apps/chat-vscode-web/index.html +1 -1
- package/dist/auth/openai-codex-usage.js +34 -5
- package/dist/core/session-router.js +94 -11
- package/dist/data/ingest-service.js +1 -1
- package/dist/data/schema.js +10 -1
- package/dist/debug/agents.js +5 -3
- package/dist/gateway/server.js +1 -1
- package/dist/session-ui/terminalRows.js +3 -1
- package/dist/sessions/pibo-data-store.js +38 -1
- package/dist/sessions/store.js +38 -0
- package/dist/shared/tool-call-metrics.js +77 -0
- package/dist/shared/trace-event-projection.js +2 -0
- package/dist/shared/trace-live-reducer.js +1 -0
- package/dist/shared/trace-patch-nodes.js +4 -0
- package/dist/subagents/context.js +2 -2
- package/dist/subagents/observation-query.js +28 -1
- package/dist/subagents/observations.js +8 -0
- package/dist/subagents/tool.js +12 -8
- package/npm-shrinkwrap.json +2 -2
- package/package.json +1 -1
- package/dist/apps/chat-ui/assets/index-BWVPNFjU.js +0 -228
- package/dist/apps/chat-ui/assets/index-CywOD6EF.css +0 -1
- package/dist/apps/chat-vscode-web/assets/index-CZmxeSn3.js +0 -43
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
6
6
|
<meta name="theme-color" content="#101d22" />
|
|
7
7
|
<title>Pibo</title>
|
|
8
|
-
<script type="module" crossorigin src="/apps/chat-vscode/assets/index-
|
|
8
|
+
<script type="module" crossorigin src="/apps/chat-vscode/assets/index-BD3Ogz9Z.js"></script>
|
|
9
9
|
<link rel="stylesheet" crossorigin href="/apps/chat-vscode/assets/index-b18ZkEo0.css">
|
|
10
10
|
</head>
|
|
11
11
|
<body>
|
|
@@ -1,7 +1,13 @@
|
|
|
1
|
+
import { createHash } from "node:crypto";
|
|
1
2
|
import { readPiCredential, resolvePiProviderAuth, } from "../agent-runtimes/pi/credentials.js";
|
|
2
3
|
const OPENAI_CODEX_PROVIDER = "openai-codex";
|
|
3
4
|
const OPENAI_CODEX_USAGE_URL = "https://chatgpt.com/backend-api/wham/usage";
|
|
4
5
|
const OPENAI_JWT_CLAIM_PATH = "https://api.openai.com/auth";
|
|
6
|
+
const USAGE_REFRESH_MS = 30_000;
|
|
7
|
+
const USAGE_MAX_STALE_MS = 5 * 60_000;
|
|
8
|
+
const USAGE_REQUEST_TIMEOUT_MS = 2_000;
|
|
9
|
+
// One bounded, credential-scoped snapshot shared by sessions. Never retain raw credentials.
|
|
10
|
+
let usageCache;
|
|
5
11
|
function isRecord(value) {
|
|
6
12
|
return Boolean(value) && typeof value === "object" && !Array.isArray(value);
|
|
7
13
|
}
|
|
@@ -125,8 +131,31 @@ export async function getOpenAiCodexProviderUsageForActiveModel(activeModel) {
|
|
|
125
131
|
if (activeModel?.provider !== OPENAI_CODEX_PROVIDER)
|
|
126
132
|
return undefined;
|
|
127
133
|
const credential = await readPiCredential(OPENAI_CODEX_PROVIDER);
|
|
128
|
-
if (credential?.type !== "oauth")
|
|
134
|
+
if (credential?.type !== "oauth") {
|
|
135
|
+
usageCache = undefined;
|
|
129
136
|
return undefined;
|
|
137
|
+
}
|
|
138
|
+
const key = createHash("sha256").update(JSON.stringify(credential)).digest("hex");
|
|
139
|
+
if (usageCache?.key !== key)
|
|
140
|
+
usageCache = { key, refreshAfter: 0 };
|
|
141
|
+
const cache = usageCache;
|
|
142
|
+
const now = Date.now();
|
|
143
|
+
const usable = () => cache.value && Date.now() - Date.parse(cache.value.fetchedAt) < USAGE_MAX_STALE_MS
|
|
144
|
+
? cache.value
|
|
145
|
+
: undefined;
|
|
146
|
+
if (now < cache.refreshAfter)
|
|
147
|
+
return usable();
|
|
148
|
+
cache.pending ??= fetchOpenAiCodexProviderUsage(credential.accountId)
|
|
149
|
+
.then((value) => { cache.value = value; return value; })
|
|
150
|
+
.catch(() => usable())
|
|
151
|
+
.finally(() => {
|
|
152
|
+
cache.refreshAfter = Date.now() + USAGE_REFRESH_MS;
|
|
153
|
+
cache.pending = undefined;
|
|
154
|
+
});
|
|
155
|
+
// Quota is advisory: stale-while-revalidate keeps repeat status commands local.
|
|
156
|
+
return usable() ?? await cache.pending;
|
|
157
|
+
}
|
|
158
|
+
async function fetchOpenAiCodexProviderUsage(storedAccountId) {
|
|
130
159
|
const resolvedAuth = await resolvePiProviderAuth(OPENAI_CODEX_PROVIDER);
|
|
131
160
|
const accessToken = resolvedAuth?.auth.apiKey;
|
|
132
161
|
if (!accessToken)
|
|
@@ -135,13 +164,13 @@ export async function getOpenAiCodexProviderUsageForActiveModel(activeModel) {
|
|
|
135
164
|
Authorization: `Bearer ${accessToken}`,
|
|
136
165
|
"User-Agent": "codex-cli",
|
|
137
166
|
};
|
|
138
|
-
const accountId = getOpenAiAccountId(accessToken,
|
|
167
|
+
const accountId = getOpenAiAccountId(accessToken, storedAccountId);
|
|
139
168
|
if (accountId)
|
|
140
169
|
headers["ChatGPT-Account-Id"] = accountId;
|
|
141
|
-
const response = await fetch(OPENAI_CODEX_USAGE_URL, { headers });
|
|
170
|
+
const response = await fetch(OPENAI_CODEX_USAGE_URL, { headers, signal: AbortSignal.timeout(USAGE_REQUEST_TIMEOUT_MS) });
|
|
142
171
|
if (!response.ok) {
|
|
143
|
-
|
|
144
|
-
throw new Error(`OpenAI Codex usage request failed: ${response.status}
|
|
172
|
+
await response.body?.cancel();
|
|
173
|
+
throw new Error(`OpenAI Codex usage request failed: ${response.status}`);
|
|
145
174
|
}
|
|
146
175
|
return normalizeUsagePayload(await response.json());
|
|
147
176
|
}
|
|
@@ -7,13 +7,13 @@ import { runtimeSessionErrorDetails } from "./session-errors.js";
|
|
|
7
7
|
import { OutputRenderSequencer, outputRenderHighWaterStore } from "./output-render-sequence.js";
|
|
8
8
|
import { normalizePiboAgentSessionName, } from "../subagents/tool.js";
|
|
9
9
|
import { piboAgentObservationDetails, piboAgentObservationKind, piboAgentObservationRole, piboAgentObservationSourceFromEvent, piboAgentObservationText, } from "../subagents/observations.js";
|
|
10
|
-
import { preparePiboAgentObservationQuery, selectPiboAgentObservationPage, } from "../subagents/observation-query.js";
|
|
10
|
+
import { piboAgentObservationCursorScopeKey, preparePiboAgentObservationQuery, selectPiboAgentObservationPage, } from "../subagents/observation-query.js";
|
|
11
11
|
import { PiboRunRegistry } from "../runs/registry.js";
|
|
12
12
|
import { PiboRunCancellationError, PiboRunCancelledError, PiboRunExecutionTimeoutError, waitForRunCancellationSettlement } from "../runs/lifecycle.js";
|
|
13
13
|
import { PiboRunResourceLimitError } from "../runs/resource-isolation.js";
|
|
14
14
|
import { createPiboSignalRegistry } from "../signals/registry.js";
|
|
15
15
|
import { createDefaultPiboReliabilityStore } from "../reliability/store.js";
|
|
16
|
-
import { InMemoryPiboSessionStore, createPiboSessionId, } from "../sessions/store.js";
|
|
16
|
+
import { PIBO_AGENT_OBSERVATION_AUTO_CURSOR_MAX_SCOPES, InMemoryPiboSessionStore, createPiboSessionId, } from "../sessions/store.js";
|
|
17
17
|
import { createAgentRuntimeBindingPersistence } from "../sessions/runtime-binding-persistence.js";
|
|
18
18
|
import { createLegacyPiRuntimeSessionBinding, RuntimeSessionBindingConflictError, } from "../sessions/runtime-binding.js";
|
|
19
19
|
import { AgentRuntimeBindingMissingError, AgentRuntimeUnavailableError } from "../agent-runtime/errors.js";
|
|
@@ -349,7 +349,9 @@ export class PiboSessionRouter {
|
|
|
349
349
|
activeSubagentRequests = new Map();
|
|
350
350
|
subagentRequestIdsByEvent = new Map();
|
|
351
351
|
agentObservations = [];
|
|
352
|
+
agentObservationHighWaterByParent = new Map();
|
|
352
353
|
agentObservationEvictedThroughByParent = new Map();
|
|
354
|
+
agentObservationAutoCursorFallback = new Map();
|
|
353
355
|
nextAgentObservationSequence = 1;
|
|
354
356
|
scheduledRunReminders = new Map();
|
|
355
357
|
runReminderDeliveries = new Map();
|
|
@@ -945,16 +947,41 @@ export class PiboSessionRouter {
|
|
|
945
947
|
const status = this.sessions.get(piboSessionId)?.getStatus();
|
|
946
948
|
return status ? this.withPersistedRuntimeBinding(status) : undefined;
|
|
947
949
|
}
|
|
948
|
-
async getSessionStatusSnapshot(piboSessionId) {
|
|
949
|
-
const session = await this.getOrCreateSession(piboSessionId);
|
|
950
|
+
async getSessionStatusSnapshot(piboSessionId, options) {
|
|
951
|
+
const session = options?.activate === false ? this.sessions.get(piboSessionId) : await this.getOrCreateSession(piboSessionId);
|
|
952
|
+
if (!session) {
|
|
953
|
+
this.resolvePiboSession(piboSessionId);
|
|
954
|
+
return undefined;
|
|
955
|
+
}
|
|
950
956
|
try {
|
|
951
957
|
return this.withPersistedRuntimeBinding(await session.getStatusSnapshot());
|
|
952
958
|
}
|
|
953
959
|
finally {
|
|
954
|
-
|
|
960
|
+
// Passive header polling must neither create nor indefinitely retain a runtime.
|
|
961
|
+
if (options?.activate !== false)
|
|
962
|
+
this.scheduleIdleSessionEvictionIfIdle(piboSessionId);
|
|
955
963
|
}
|
|
956
964
|
}
|
|
957
965
|
async getSessionForkCandidates(piboSessionId) {
|
|
966
|
+
const canReadPersisted = () => !this.closing
|
|
967
|
+
&& !this.quiescingSessions.has(piboSessionId)
|
|
968
|
+
&& !this.disposingSessions.has(piboSessionId)
|
|
969
|
+
&& !this.sessions.has(piboSessionId)
|
|
970
|
+
&& !this.pendingSessions.has(piboSessionId);
|
|
971
|
+
if (canReadPersisted()) {
|
|
972
|
+
const record = this.resolvePiboSession(piboSessionId);
|
|
973
|
+
const binding = this.resolveSessionRuntimeBinding(record);
|
|
974
|
+
const adapter = this.resolveAgentRuntimeRegistry(binding.runtimeInstanceId).requireAgentRuntimeAdapter(binding.runtimeInstanceId);
|
|
975
|
+
if (binding.state === "bound" && adapter.descriptor.id === binding.adapterId
|
|
976
|
+
&& adapter.descriptor.capabilities.lifecycle.fork && adapter.readForkCandidates) {
|
|
977
|
+
const workspace = record.workspace ?? this.options.cwd ?? getDefaultPiboWorkspace();
|
|
978
|
+
const candidates = await adapter.readForkCandidates({ binding, workspace });
|
|
979
|
+
const current = this.resolvePiboSession(piboSessionId);
|
|
980
|
+
if (candidates !== undefined && canReadPersisted() && current.workspace === record.workspace
|
|
981
|
+
&& runtimeBindingsEqual(binding, this.resolveSessionRuntimeBinding(current)))
|
|
982
|
+
return candidates;
|
|
983
|
+
}
|
|
984
|
+
}
|
|
958
985
|
const session = await this.getOrCreateSession(piboSessionId);
|
|
959
986
|
try {
|
|
960
987
|
return await session.getForkCandidates();
|
|
@@ -1146,7 +1173,9 @@ export class PiboSessionRouter {
|
|
|
1146
1173
|
this.subagentRequestIdsByEvent.clear();
|
|
1147
1174
|
this.outputRenderSequencer.disposeAll();
|
|
1148
1175
|
this.agentObservations.length = 0;
|
|
1176
|
+
this.agentObservationHighWaterByParent.clear();
|
|
1149
1177
|
this.agentObservationEvictedThroughByParent.clear();
|
|
1178
|
+
this.agentObservationAutoCursorFallback.clear();
|
|
1150
1179
|
await this.telemetryWriter?.dispose();
|
|
1151
1180
|
const lifecycleFailures = [...authDisposals, ...webAppDisposals]
|
|
1152
1181
|
.flatMap((result) => result.status === "rejected" ? [result.reason] : []);
|
|
@@ -2098,8 +2127,18 @@ export class PiboSessionRouter {
|
|
|
2098
2127
|
observeManagedAgents(parentPiboSessionId, input) {
|
|
2099
2128
|
for (const agentId of input.agentIds ?? [])
|
|
2100
2129
|
this.requireManagedAgent(parentPiboSessionId, agentId);
|
|
2101
|
-
const
|
|
2130
|
+
const baseQuery = preparePiboAgentObservationQuery(input);
|
|
2131
|
+
const cursorScope = piboAgentObservationCursorScopeKey(baseQuery.filters);
|
|
2132
|
+
const explicitAfterSequence = input.afterSequence !== undefined;
|
|
2133
|
+
const savedAfterSequence = baseQuery.cursorMode === "auto" && !explicitAfterSequence
|
|
2134
|
+
? this.getAgentObservationAutoCursor(parentPiboSessionId, cursorScope)
|
|
2135
|
+
: undefined;
|
|
2136
|
+
const query = savedAfterSequence === undefined
|
|
2137
|
+
? baseQuery
|
|
2138
|
+
: preparePiboAgentObservationQuery({ ...input, afterSequence: savedAfterSequence });
|
|
2102
2139
|
const observations = this.agentObservations;
|
|
2140
|
+
const evictedThrough = this.agentObservationEvictedThroughByParent.get(parentPiboSessionId) ?? 0;
|
|
2141
|
+
const sourceHighWater = Math.max(evictedThrough, this.agentObservationHighWaterByParent.get(parentPiboSessionId) ?? 0);
|
|
2103
2142
|
function* ordered() {
|
|
2104
2143
|
const start = query.scanOrder === "asc" ? 0 : observations.length - 1;
|
|
2105
2144
|
const end = query.scanOrder === "asc" ? observations.length : -1;
|
|
@@ -2110,7 +2149,48 @@ export class PiboSessionRouter {
|
|
|
2110
2149
|
yield observation;
|
|
2111
2150
|
}
|
|
2112
2151
|
}
|
|
2113
|
-
|
|
2152
|
+
const page = selectPiboAgentObservationPage(ordered(), query, { evictedThrough });
|
|
2153
|
+
if (query.cursorMode === "history")
|
|
2154
|
+
return page;
|
|
2155
|
+
const initialSnapshot = !explicitAfterSequence && savedAfterSequence === undefined;
|
|
2156
|
+
const nextAfterSequence = initialSnapshot || !page.truncated
|
|
2157
|
+
? Math.max(page.nextAfterSequence, sourceHighWater)
|
|
2158
|
+
: page.nextAfterSequence;
|
|
2159
|
+
const advancedAfterSequence = this.advanceAgentObservationAutoCursor(parentPiboSessionId, cursorScope, nextAfterSequence);
|
|
2160
|
+
return {
|
|
2161
|
+
...page,
|
|
2162
|
+
autoCursorSequence: advancedAfterSequence,
|
|
2163
|
+
};
|
|
2164
|
+
}
|
|
2165
|
+
getAgentObservationAutoCursor(parentPiboSessionId, cursorScope) {
|
|
2166
|
+
if (this.sessionStore.getAgentObservationAutoCursor) {
|
|
2167
|
+
return this.sessionStore.getAgentObservationAutoCursor(parentPiboSessionId, cursorScope);
|
|
2168
|
+
}
|
|
2169
|
+
return this.agentObservationAutoCursorFallback.get(JSON.stringify([parentPiboSessionId, cursorScope]));
|
|
2170
|
+
}
|
|
2171
|
+
advanceAgentObservationAutoCursor(parentPiboSessionId, cursorScope, sequence) {
|
|
2172
|
+
if (this.sessionStore.advanceAgentObservationAutoCursor) {
|
|
2173
|
+
return this.sessionStore.advanceAgentObservationAutoCursor(parentPiboSessionId, cursorScope, sequence);
|
|
2174
|
+
}
|
|
2175
|
+
const key = JSON.stringify([parentPiboSessionId, cursorScope]);
|
|
2176
|
+
const advanced = Math.max(this.agentObservationAutoCursorFallback.get(key) ?? 0, sequence);
|
|
2177
|
+
this.agentObservationAutoCursorFallback.delete(key);
|
|
2178
|
+
this.agentObservationAutoCursorFallback.set(key, advanced);
|
|
2179
|
+
const prefix = `${JSON.stringify([parentPiboSessionId]).slice(0, -1)},`;
|
|
2180
|
+
let scopeCount = 0;
|
|
2181
|
+
for (const existingKey of this.agentObservationAutoCursorFallback.keys()) {
|
|
2182
|
+
if (existingKey.startsWith(prefix))
|
|
2183
|
+
scopeCount += 1;
|
|
2184
|
+
}
|
|
2185
|
+
for (const existingKey of this.agentObservationAutoCursorFallback.keys()) {
|
|
2186
|
+
if (scopeCount <= PIBO_AGENT_OBSERVATION_AUTO_CURSOR_MAX_SCOPES)
|
|
2187
|
+
break;
|
|
2188
|
+
if (!existingKey.startsWith(prefix))
|
|
2189
|
+
continue;
|
|
2190
|
+
this.agentObservationAutoCursorFallback.delete(existingKey);
|
|
2191
|
+
scopeCount -= 1;
|
|
2192
|
+
}
|
|
2193
|
+
return advanced;
|
|
2114
2194
|
}
|
|
2115
2195
|
async killManagedAgent(parentPiboSessionId, agentId) {
|
|
2116
2196
|
const child = this.requireManagedAgent(parentPiboSessionId, agentId);
|
|
@@ -2300,16 +2380,16 @@ export class PiboSessionRouter {
|
|
|
2300
2380
|
throw new Error(`Subagent "${subagent.name}" exceeded max depth ${maxDepth} from Pibo session "${parentPiboSessionId}"`);
|
|
2301
2381
|
}
|
|
2302
2382
|
}
|
|
2303
|
-
getSubagentDepth(piboSessionId) {
|
|
2383
|
+
getSubagentDepth(piboSessionId, sessionsById) {
|
|
2304
2384
|
let depth = 0;
|
|
2305
|
-
let current = this.sessionStore.get(piboSessionId);
|
|
2385
|
+
let current = sessionsById ? sessionsById.get(piboSessionId) : this.sessionStore.get(piboSessionId);
|
|
2306
2386
|
const seen = new Set();
|
|
2307
2387
|
while (current?.parentId) {
|
|
2308
2388
|
if (seen.has(current.parentId))
|
|
2309
2389
|
break;
|
|
2310
2390
|
seen.add(current.parentId);
|
|
2311
2391
|
depth += 1;
|
|
2312
|
-
current = this.sessionStore.get(current.parentId);
|
|
2392
|
+
current = sessionsById ? sessionsById.get(current.parentId) : this.sessionStore.get(current.parentId);
|
|
2313
2393
|
}
|
|
2314
2394
|
return depth;
|
|
2315
2395
|
}
|
|
@@ -2412,6 +2492,7 @@ export class PiboSessionRouter {
|
|
|
2412
2492
|
details: piboAgentObservationDetails(event),
|
|
2413
2493
|
};
|
|
2414
2494
|
this.agentObservations.push(observation);
|
|
2495
|
+
this.agentObservationHighWaterByParent.set(session.parentId, Math.max(this.agentObservationHighWaterByParent.get(session.parentId) ?? 0, sequence));
|
|
2415
2496
|
if (this.agentObservations.length > MAX_AGENT_OBSERVATIONS) {
|
|
2416
2497
|
const evicted = this.agentObservations.splice(0, this.agentObservations.length - MAX_AGENT_OBSERVATIONS);
|
|
2417
2498
|
for (const item of evicted) {
|
|
@@ -2567,7 +2648,9 @@ export class PiboSessionRouter {
|
|
|
2567
2648
|
}
|
|
2568
2649
|
projectKnownSessionSignals() {
|
|
2569
2650
|
const sessions = this.sessionStore.list?.() ?? [];
|
|
2570
|
-
|
|
2651
|
+
// The complete list is already loaded; avoid an additional store query for every ancestor.
|
|
2652
|
+
const sessionsById = new Map(sessions.map((session) => [session.id, session]));
|
|
2653
|
+
const depthBySessionId = new Map(sessions.map((session) => [session.id, this.getSubagentDepth(session.id, sessionsById)]));
|
|
2571
2654
|
sessions.sort((left, right) => (depthBySessionId.get(left.id) ?? 0) - (depthBySessionId.get(right.id) ?? 0));
|
|
2572
2655
|
for (const session of sessions) {
|
|
2573
2656
|
this.signalRegistry.project({ type: "session_created", session });
|
|
@@ -427,7 +427,7 @@ function specificAttributesForOutputEvent(event) {
|
|
|
427
427
|
if (event.type === "tool_call")
|
|
428
428
|
return { toolCallId: event.toolCallId, toolName: event.toolName, argsComplete: event.argsComplete, intent: event.intent };
|
|
429
429
|
if (event.type === "tool_execution_started" || event.type === "tool_execution_updated" || event.type === "tool_execution_finished")
|
|
430
|
-
return { toolCallId: event.toolCallId, toolName: event.toolName, isError: "isError" in event ? event.isError : undefined, intent: event.intent };
|
|
430
|
+
return { toolCallId: event.toolCallId, toolName: event.toolName, isError: "isError" in event ? event.isError : undefined, intent: event.intent, toolMetrics: event.type === "tool_execution_finished" ? event.toolMetrics : undefined };
|
|
431
431
|
if (event.type === "subagent_session")
|
|
432
432
|
return { toolCallId: event.toolCallId, toolName: event.toolName, subagentName: event.subagentName, childPiboSessionId: event.childPiboSessionId, threadKey: event.threadKey };
|
|
433
433
|
if (event.type === "execution_result")
|
package/dist/data/schema.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export const PIBO_DATA_SCHEMA_VERSION =
|
|
1
|
+
export const PIBO_DATA_SCHEMA_VERSION = 9;
|
|
2
2
|
const NATIVE_HISTORY_FALLBACK_SCHEMA_VERSION = 5;
|
|
3
3
|
const retiredScopeColumn = ["owner", "scope"].join("_");
|
|
4
4
|
const payloadTableDefinition = `
|
|
@@ -254,6 +254,15 @@ function applyPiboDataSchemaInTransaction(db, hooks, previousVersion, tablesToRe
|
|
|
254
254
|
FOREIGN KEY (parent_pibo_session_id) REFERENCES sessions(id) ON DELETE CASCADE
|
|
255
255
|
);
|
|
256
256
|
|
|
257
|
+
CREATE TABLE IF NOT EXISTS session_agent_observation_auto_cursors (
|
|
258
|
+
parent_pibo_session_id TEXT NOT NULL,
|
|
259
|
+
cursor_scope TEXT NOT NULL,
|
|
260
|
+
sequence INTEGER NOT NULL CHECK(sequence >= 0),
|
|
261
|
+
updated_at TEXT NOT NULL,
|
|
262
|
+
PRIMARY KEY (parent_pibo_session_id, cursor_scope),
|
|
263
|
+
FOREIGN KEY (parent_pibo_session_id) REFERENCES sessions(id) ON DELETE CASCADE
|
|
264
|
+
);
|
|
265
|
+
|
|
257
266
|
CREATE TABLE IF NOT EXISTS session_output_part_counters (
|
|
258
267
|
pibo_session_id TEXT NOT NULL,
|
|
259
268
|
event_id TEXT NOT NULL,
|
package/dist/debug/agents.js
CHANGED
|
@@ -81,7 +81,7 @@ export function inspectDebugAgentObservations(parentPiboSessionId, store, input
|
|
|
81
81
|
throw new Error(`Debug store "pibo-data" not found at ${store.path}`);
|
|
82
82
|
const db = openReadOnlyDebugDatabase(store);
|
|
83
83
|
try {
|
|
84
|
-
const query = preparePiboAgentObservationQuery(input);
|
|
84
|
+
const query = preparePiboAgentObservationQuery({ ...input, cursorMode: "history" });
|
|
85
85
|
const owned = readOwnedAgents(db, parentPiboSessionId);
|
|
86
86
|
const ownedById = new Map(owned.map((agent) => [agent.agentId, agent]));
|
|
87
87
|
for (const agentId of input.agentIds ?? []) {
|
|
@@ -287,8 +287,10 @@ Usage:
|
|
|
287
287
|
[--order asc|desc] [--limit 1..200] [--include-tools]
|
|
288
288
|
[--tool-detail summary|full] [--details] [--json]
|
|
289
289
|
|
|
290
|
-
Default: the newest 20 completed assistant messages
|
|
291
|
-
|
|
290
|
+
Default: the newest 20 completed assistant messages; streaming deltas and tools are hidden.
|
|
291
|
+
The CLI is stateless history inspection. Use --after-sequence for explicit incremental polling.
|
|
292
|
+
Use --include-tools only for stalls, errors, or targeted diagnosis; prefer --tool-call-id when known.
|
|
293
|
+
Explicit --event-type or --kind
|
|
292
294
|
filters retain access to progress events. Repeat plural filters for OR within that field.
|
|
293
295
|
Different fields combine with AND. With --after-sequence, pages always consume the oldest unseen rows;
|
|
294
296
|
--order desc reverses only the returned page, so nextAfterSequence remains safe for polling.
|
package/dist/gateway/server.js
CHANGED
|
@@ -421,7 +421,7 @@ export class PiboGatewayServer {
|
|
|
421
421
|
},
|
|
422
422
|
rebindSessionRuntime: (piboSessionId, input) => this.requireRouter().rebindSessionRuntime(piboSessionId, input),
|
|
423
423
|
getSessionRuntimeStatus: (piboSessionId) => this.requireRouter().getSessionRuntimeStatus(piboSessionId),
|
|
424
|
-
getSessionStatusSnapshot: (piboSessionId) => this.requireRouter().getSessionStatusSnapshot(piboSessionId),
|
|
424
|
+
getSessionStatusSnapshot: (piboSessionId, options) => this.requireRouter().getSessionStatusSnapshot(piboSessionId, options),
|
|
425
425
|
getSessionForkCandidates: (piboSessionId) => this.requireRouter().getSessionForkCandidates(piboSessionId),
|
|
426
426
|
listSessionRuntimeStatuses: () => this.requireRouter().listSessionRuntimeStatuses(),
|
|
427
427
|
listRuns: (options) => this.requireRouter().listRuns(options),
|
|
@@ -22,7 +22,7 @@ export function buildCompactTerminalRows(traceView, options) {
|
|
|
22
22
|
const candidates = syncThinkingToolRows(flatNodes.map((item) => createRowCandidate(item.node, item.turnId)));
|
|
23
23
|
applyCompletedTurnTiming(candidates, turnById);
|
|
24
24
|
const reconciled = reconcileConceptualRowCandidates(candidates);
|
|
25
|
-
const rows = (options.toolDisplayMode ?? "default") === "default"
|
|
25
|
+
const rows = !options.debugMode && (options.toolDisplayMode ?? "default") === "default"
|
|
26
26
|
? groupRelatedToolCandidates(reconciled).map((candidate) => candidate.row)
|
|
27
27
|
: reconciled.map((candidate) => candidate.row);
|
|
28
28
|
return applyToolDisplayMode(rows, options.toolDisplayMode ?? "default");
|
|
@@ -165,6 +165,8 @@ function createRowCandidate(node, turnId) {
|
|
|
165
165
|
...candidate.row,
|
|
166
166
|
id: compactTerminalRowIdentity(node),
|
|
167
167
|
intent: node.intent,
|
|
168
|
+
isToolCall: node.type === "tool.call" || node.type === "tool.result" || (node.type === "agent.delegation" && Boolean(node.toolCallId)),
|
|
169
|
+
toolMetrics: node.toolMetrics,
|
|
168
170
|
...debugFields(node),
|
|
169
171
|
},
|
|
170
172
|
};
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { ChatDataIngestService } from "../data/ingest-service.js";
|
|
2
2
|
import { PiboDataStore } from "../data/pibo-store.js";
|
|
3
|
-
import { createPiboSession, matchesFindInput, } from "./store.js";
|
|
3
|
+
import { PIBO_AGENT_OBSERVATION_AUTO_CURSOR_MAX_SCOPES, createPiboSession, matchesFindInput, } from "./store.js";
|
|
4
4
|
import { createLegacyPiRuntimeSessionBinding, nextRuntimeSessionBinding, RuntimeSessionBindingConflictError, } from "./runtime-binding.js";
|
|
5
5
|
const SESSION_SELECT = `
|
|
6
6
|
SELECT
|
|
@@ -165,6 +165,43 @@ export class PiboDataSessionStore {
|
|
|
165
165
|
return sequence;
|
|
166
166
|
});
|
|
167
167
|
}
|
|
168
|
+
getAgentObservationAutoCursor(parentPiboSessionId, cursorScope) {
|
|
169
|
+
const row = this.db.prepare(`
|
|
170
|
+
SELECT sequence
|
|
171
|
+
FROM session_agent_observation_auto_cursors
|
|
172
|
+
WHERE parent_pibo_session_id = ? AND cursor_scope = ?
|
|
173
|
+
`).get(parentPiboSessionId, cursorScope);
|
|
174
|
+
return row?.sequence;
|
|
175
|
+
}
|
|
176
|
+
advanceAgentObservationAutoCursor(parentPiboSessionId, cursorScope, sequence) {
|
|
177
|
+
return this.dataStore.transaction(() => {
|
|
178
|
+
this.db.prepare(`
|
|
179
|
+
INSERT INTO session_agent_observation_auto_cursors (
|
|
180
|
+
parent_pibo_session_id, cursor_scope, sequence, updated_at
|
|
181
|
+
)
|
|
182
|
+
SELECT id, ?, ?, ? FROM sessions WHERE id = ? AND deleted_at IS NULL
|
|
183
|
+
ON CONFLICT(parent_pibo_session_id, cursor_scope) DO UPDATE SET
|
|
184
|
+
sequence = MAX(session_agent_observation_auto_cursors.sequence, excluded.sequence),
|
|
185
|
+
updated_at = CASE
|
|
186
|
+
WHEN excluded.sequence > session_agent_observation_auto_cursors.sequence THEN excluded.updated_at
|
|
187
|
+
ELSE session_agent_observation_auto_cursors.updated_at
|
|
188
|
+
END
|
|
189
|
+
`).run(cursorScope, sequence, new Date().toISOString(), parentPiboSessionId);
|
|
190
|
+
this.db.prepare(`
|
|
191
|
+
DELETE FROM session_agent_observation_auto_cursors
|
|
192
|
+
WHERE parent_pibo_session_id = ?
|
|
193
|
+
AND cursor_scope <> ?
|
|
194
|
+
AND cursor_scope NOT IN (
|
|
195
|
+
SELECT cursor_scope
|
|
196
|
+
FROM session_agent_observation_auto_cursors
|
|
197
|
+
WHERE parent_pibo_session_id = ? AND cursor_scope <> ?
|
|
198
|
+
ORDER BY updated_at DESC, cursor_scope DESC
|
|
199
|
+
LIMIT ?
|
|
200
|
+
)
|
|
201
|
+
`).run(parentPiboSessionId, cursorScope, parentPiboSessionId, cursorScope, PIBO_AGENT_OBSERVATION_AUTO_CURSOR_MAX_SCOPES - 1);
|
|
202
|
+
return this.getAgentObservationAutoCursor(parentPiboSessionId, cursorScope) ?? sequence;
|
|
203
|
+
});
|
|
204
|
+
}
|
|
168
205
|
claimOrAttachOutputPart(input) {
|
|
169
206
|
return this.dataStore.transaction(() => {
|
|
170
207
|
const indexAttribute = outputPartIndexAttribute(input.kind);
|
package/dist/sessions/store.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { randomUUID } from "node:crypto";
|
|
2
2
|
import { createInitialRuntimeSessionBinding, createLegacyPiRuntimeSessionBinding, nextRuntimeSessionBinding, } from "./runtime-binding.js";
|
|
3
|
+
export const PIBO_AGENT_OBSERVATION_AUTO_CURSOR_MAX_SCOPES = 128;
|
|
3
4
|
export function createPiboSessionId() {
|
|
4
5
|
return `ps_${randomUUID()}`;
|
|
5
6
|
}
|
|
@@ -50,6 +51,7 @@ export class InMemoryPiboSessionStore {
|
|
|
50
51
|
byNativeSession = new Map();
|
|
51
52
|
outputRenderHighWater = new Map();
|
|
52
53
|
agentObservationNextSequence = new Map();
|
|
54
|
+
agentObservationAutoCursors = new Map();
|
|
53
55
|
outputToolInvocationNextOrdinal = new Map();
|
|
54
56
|
get(id) {
|
|
55
57
|
return this.byId.get(id);
|
|
@@ -128,6 +130,34 @@ export class InMemoryPiboSessionStore {
|
|
|
128
130
|
this.agentObservationNextSequence.set(parentPiboSessionId, sequence + 1);
|
|
129
131
|
return sequence;
|
|
130
132
|
}
|
|
133
|
+
getAgentObservationAutoCursor(parentPiboSessionId, cursorScope) {
|
|
134
|
+
if (!this.byId.has(parentPiboSessionId))
|
|
135
|
+
return undefined;
|
|
136
|
+
return this.agentObservationAutoCursors.get(agentObservationAutoCursorKey(parentPiboSessionId, cursorScope));
|
|
137
|
+
}
|
|
138
|
+
advanceAgentObservationAutoCursor(parentPiboSessionId, cursorScope, sequence) {
|
|
139
|
+
if (!this.byId.has(parentPiboSessionId))
|
|
140
|
+
return sequence;
|
|
141
|
+
const key = agentObservationAutoCursorKey(parentPiboSessionId, cursorScope);
|
|
142
|
+
const advanced = Math.max(this.agentObservationAutoCursors.get(key) ?? 0, sequence);
|
|
143
|
+
this.agentObservationAutoCursors.delete(key);
|
|
144
|
+
this.agentObservationAutoCursors.set(key, advanced);
|
|
145
|
+
const prefix = `${JSON.stringify([parentPiboSessionId]).slice(0, -1)},`;
|
|
146
|
+
let scopeCount = 0;
|
|
147
|
+
for (const existingKey of this.agentObservationAutoCursors.keys()) {
|
|
148
|
+
if (existingKey.startsWith(prefix))
|
|
149
|
+
scopeCount += 1;
|
|
150
|
+
}
|
|
151
|
+
for (const existingKey of this.agentObservationAutoCursors.keys()) {
|
|
152
|
+
if (scopeCount <= PIBO_AGENT_OBSERVATION_AUTO_CURSOR_MAX_SCOPES)
|
|
153
|
+
break;
|
|
154
|
+
if (!existingKey.startsWith(prefix))
|
|
155
|
+
continue;
|
|
156
|
+
this.agentObservationAutoCursors.delete(existingKey);
|
|
157
|
+
scopeCount -= 1;
|
|
158
|
+
}
|
|
159
|
+
return advanced;
|
|
160
|
+
}
|
|
131
161
|
claimOutputToolInvocationOrdinal(piboSessionId, eventId, toolCallId) {
|
|
132
162
|
const key = outputToolInvocationCounterKey(piboSessionId, eventId, toolCallId);
|
|
133
163
|
const ordinal = this.outputToolInvocationNextOrdinal.get(key) ?? 0;
|
|
@@ -172,6 +202,11 @@ export class InMemoryPiboSessionStore {
|
|
|
172
202
|
this.byNativeSession.delete(nativeKey);
|
|
173
203
|
this.outputRenderHighWater.delete(id);
|
|
174
204
|
this.agentObservationNextSequence.delete(id);
|
|
205
|
+
const observationCursorPrefix = `${JSON.stringify([id]).slice(0, -1)},`;
|
|
206
|
+
for (const key of this.agentObservationAutoCursors.keys()) {
|
|
207
|
+
if (key.startsWith(observationCursorPrefix))
|
|
208
|
+
this.agentObservationAutoCursors.delete(key);
|
|
209
|
+
}
|
|
175
210
|
const counterPrefix = `${JSON.stringify([id]).slice(0, -1)},`;
|
|
176
211
|
for (const key of this.outputToolInvocationNextOrdinal.keys()) {
|
|
177
212
|
if (key.startsWith(counterPrefix))
|
|
@@ -213,6 +248,9 @@ function outputRenderSequenceHighWater(metadata) {
|
|
|
213
248
|
const value = metadata?.outputRenderSequenceHighWater;
|
|
214
249
|
return typeof value === "number" && Number.isSafeInteger(value) && value >= 0 ? value : 0;
|
|
215
250
|
}
|
|
251
|
+
function agentObservationAutoCursorKey(parentPiboSessionId, cursorScope) {
|
|
252
|
+
return JSON.stringify([parentPiboSessionId, cursorScope]);
|
|
253
|
+
}
|
|
216
254
|
function outputToolInvocationCounterKey(piboSessionId, eventId, toolCallId) {
|
|
217
255
|
return JSON.stringify([piboSessionId, eventId, toolCallId]);
|
|
218
256
|
}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
/** Bounded structural walk; never tokenize, copy or scan large strings. */
|
|
2
|
+
export function estimateToolPayloadTokens(payload) {
|
|
3
|
+
let budget = 10_000;
|
|
4
|
+
const seen = new WeakSet();
|
|
5
|
+
function size(value, depth) {
|
|
6
|
+
if (--budget < 0 || depth > 64)
|
|
7
|
+
return NaN;
|
|
8
|
+
if (typeof value === "string")
|
|
9
|
+
return value.length;
|
|
10
|
+
if (value === null)
|
|
11
|
+
return 4;
|
|
12
|
+
if (typeof value === "boolean")
|
|
13
|
+
return value ? 4 : 5;
|
|
14
|
+
if (typeof value === "number")
|
|
15
|
+
return String(value).length;
|
|
16
|
+
if (typeof value !== "object" || seen.has(value))
|
|
17
|
+
return NaN;
|
|
18
|
+
seen.add(value);
|
|
19
|
+
let chars = 2;
|
|
20
|
+
if (Array.isArray(value)) {
|
|
21
|
+
for (const item of value) {
|
|
22
|
+
chars += size(item, depth + 1) + 1;
|
|
23
|
+
if (!Number.isFinite(chars))
|
|
24
|
+
return NaN;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
else {
|
|
28
|
+
const record = value;
|
|
29
|
+
// Binary/media payloads have no meaningful character-based token count.
|
|
30
|
+
if (["image", "audio", "document", "resource", "image_url"].includes(String(record.type)))
|
|
31
|
+
return NaN;
|
|
32
|
+
for (const key in record) {
|
|
33
|
+
if (--budget < 0)
|
|
34
|
+
return NaN;
|
|
35
|
+
if (!Object.hasOwn(record, key) || record[key] === undefined)
|
|
36
|
+
continue;
|
|
37
|
+
chars += key.length + 3 + size(record[key], depth + 1) + 1;
|
|
38
|
+
if (!Number.isFinite(chars))
|
|
39
|
+
return NaN;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
seen.delete(value);
|
|
43
|
+
return chars;
|
|
44
|
+
}
|
|
45
|
+
try {
|
|
46
|
+
const chars = size(payload, 0);
|
|
47
|
+
return Number.isFinite(chars) ? Math.ceil(chars / 4) : undefined;
|
|
48
|
+
}
|
|
49
|
+
catch {
|
|
50
|
+
// Diagnostics must not turn an otherwise successful tool into a failure.
|
|
51
|
+
return undefined;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
export class ToolCallMetricsCollector {
|
|
55
|
+
active = new Map();
|
|
56
|
+
start(id, args, now = performance.now()) {
|
|
57
|
+
if (this.active.has(id))
|
|
58
|
+
return;
|
|
59
|
+
this.active.set(id, { startedAt: now, inputTokens: estimateToolPayloadTokens(args) });
|
|
60
|
+
}
|
|
61
|
+
finish(id, result, now = performance.now()) {
|
|
62
|
+
const started = this.active.get(id);
|
|
63
|
+
this.active.delete(id);
|
|
64
|
+
// Harness result metadata is not model-visible tool output.
|
|
65
|
+
const output = result && typeof result === "object" && "content" in result
|
|
66
|
+
? result.content : result;
|
|
67
|
+
return {
|
|
68
|
+
tokenBasis: "chars/4",
|
|
69
|
+
durationMs: started ? Math.max(0, now - started.startedAt) : undefined,
|
|
70
|
+
inputTokens: started?.inputTokens,
|
|
71
|
+
outputTokens: estimateToolPayloadTokens(output),
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
clear() {
|
|
75
|
+
this.active.clear();
|
|
76
|
+
}
|
|
77
|
+
}
|
|
@@ -626,6 +626,7 @@ function traceNodeFromEvent(piboSessionId, event, childByParent, linkedChildByTo
|
|
|
626
626
|
toolCallId: event.toolCallId,
|
|
627
627
|
toolInvocationOrdinal: event.toolInvocationOrdinal ?? 0,
|
|
628
628
|
intent: event.intent,
|
|
629
|
+
toolMetrics: event.type === "tool_execution_finished" ? event.toolMetrics : undefined,
|
|
629
630
|
type: subagentTool ? "agent.delegation" : "tool.call",
|
|
630
631
|
title: event.toolName,
|
|
631
632
|
status: event.type === "tool_execution_finished"
|
|
@@ -1102,6 +1103,7 @@ function thinkingEventNodeId(event) {
|
|
|
1102
1103
|
function mergeToolEvent(target, update) {
|
|
1103
1104
|
target.status = update.status;
|
|
1104
1105
|
target.intent = update.intent ?? target.intent;
|
|
1106
|
+
target.toolMetrics = update.toolMetrics ?? target.toolMetrics;
|
|
1105
1107
|
target.summary = update.summary ?? target.summary;
|
|
1106
1108
|
target.input = mergeDelegationInput(target, update);
|
|
1107
1109
|
target.output = update.output ?? target.output;
|
|
@@ -122,6 +122,7 @@ function storedEventFromStreamEvent(event, piboSessionId, nextSequence, now) {
|
|
|
122
122
|
toolName: event.toolName,
|
|
123
123
|
toolInvocationOrdinal: validOrdinal(event.toolInvocationOrdinal) ? event.toolInvocationOrdinal : undefined,
|
|
124
124
|
result: event.result,
|
|
125
|
+
toolMetrics: event.toolMetrics,
|
|
125
126
|
isError: Boolean(event.isError),
|
|
126
127
|
...(event.intent ? { intent: event.intent } : {}),
|
|
127
128
|
};
|
|
@@ -60,6 +60,10 @@ function traceNodeShallowEqual(left, right) {
|
|
|
60
60
|
left.startedAt === right.startedAt &&
|
|
61
61
|
left.completedAt === right.completedAt &&
|
|
62
62
|
left.durationMs === right.durationMs &&
|
|
63
|
+
left.toolMetrics?.durationMs === right.toolMetrics?.durationMs &&
|
|
64
|
+
left.toolMetrics?.inputTokens === right.toolMetrics?.inputTokens &&
|
|
65
|
+
left.toolMetrics?.outputTokens === right.toolMetrics?.outputTokens &&
|
|
66
|
+
left.toolMetrics?.tokenBasis === right.toolMetrics?.tokenBasis &&
|
|
63
67
|
left.summary === right.summary &&
|
|
64
68
|
left.input === right.input &&
|
|
65
69
|
left.output === right.output &&
|
|
@@ -33,14 +33,14 @@ export function getDelegatedAgentContextFile(subagents) {
|
|
|
33
33
|
"",
|
|
34
34
|
"pibo_run_wait({ runId, timeoutMs? }) # bounded wait only; expiry does not stop the child",
|
|
35
35
|
"pibo_run_status({ runId }) # compact lifecycle state",
|
|
36
|
-
"pibo_agents_observe({ requestIds?: [runId], textContains?, textRegex?, afterSequence?, limit?, includeTools?, toolDetail?, ... })",
|
|
36
|
+
"pibo_agents_observe({ requestIds?: [runId], cursorMode?: \"auto\"|\"history\", textContains?, textRegex?, afterSequence?, limit?, includeTools?, toolDetail?, ... })",
|
|
37
37
|
"pibo_run_read({ runId }) # terminal result, including the complete final agent message",
|
|
38
38
|
"pibo_run_cancel({ runId }) # explicit request cancellation",
|
|
39
39
|
"pibo_agents_list_agents({}) # available definitions and persistent child instances",
|
|
40
40
|
"pibo_agents_kill({ agentId }) # terminate one persistent child session subtree",
|
|
41
41
|
"```",
|
|
42
42
|
"",
|
|
43
|
-
"Set `sessionName` on every send to a nonblank human-readable child title of at most 40 Unicode code points. Pibo trims surrounding whitespace and rejects missing, blank, non-string, or oversized names before creating a yielded run or child session. Reuse a stable `threadKey` to continue the same child Pibo Session; a new `sessionName` updates its title without changing identity. A wait timeout
|
|
43
|
+
"Set `sessionName` on every send to a nonblank human-readable child title of at most 40 Unicode code points. Pibo trims surrounding whitespace and rejects missing, blank, non-string, or oversized names before creating a yielded run or child session. Reuse a stable `threadKey` to continue the same child Pibo Session; a new `sessionName` updates its title without changing identity. A wait timeout only wakes the orchestrator. Observe uses `cursorMode: \"auto\"` by default: the first equivalent query returns the newest completed assistant messages, and later calls return only unread messages. Use `cursorMode: \"history\"` only to reread earlier observations. Streaming deltas, duplicate tool progress events, and tools are hidden by default. Inspect tools only when a child stalls, reports an error, or needs targeted diagnosis; prefer exact `toolCallIds`, then `includeTools: true`, and use `toolDetail: \"full\"` only when summaries are insufficient. Use `textContains` for case-insensitive substring matching or `textRegex` for rg/Rust-regex matching; both must match when supplied together. Text, regex, identity, and event filters create separate automatic query cursors; an explicit `afterSequence` overrides and advances the matching automatic cursor.",
|
|
44
44
|
"",
|
|
45
45
|
"Observe progress and decide whether to continue waiting, steer through a new message after the current turn, cancel the request, or kill the child session.",
|
|
46
46
|
"",
|