@f5-sales-demo/xcsh 22.7.1 → 22.7.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.
|
@@ -17,17 +17,17 @@ export interface BuildInfo {
|
|
|
17
17
|
}
|
|
18
18
|
|
|
19
19
|
export const BUILD_INFO: BuildInfo = {
|
|
20
|
-
"version": "22.7.
|
|
21
|
-
"commit": "
|
|
22
|
-
"shortCommit": "
|
|
20
|
+
"version": "22.7.2",
|
|
21
|
+
"commit": "fb6e63af2545331d0ae899f6faae6588af086b92",
|
|
22
|
+
"shortCommit": "fb6e63a",
|
|
23
23
|
"branch": "main",
|
|
24
|
-
"tag": "v22.7.
|
|
25
|
-
"commitDate": "2026-10-
|
|
26
|
-
"buildDate": "2026-10-
|
|
24
|
+
"tag": "v22.7.2",
|
|
25
|
+
"commitDate": "2026-10-01T16:08:52-04:00",
|
|
26
|
+
"buildDate": "2026-10-01T20:55:11.342Z",
|
|
27
27
|
"dirty": true,
|
|
28
28
|
"prNumber": "",
|
|
29
29
|
"repoUrl": "https://github.com/f5-sales-demo/xcsh",
|
|
30
30
|
"repoSlug": "f5-sales-demo/xcsh",
|
|
31
|
-
"commitUrl": "https://github.com/f5-sales-demo/xcsh/commit/
|
|
32
|
-
"releaseUrl": "https://github.com/f5-sales-demo/xcsh/releases/tag/v22.7.
|
|
31
|
+
"commitUrl": "https://github.com/f5-sales-demo/xcsh/commit/fb6e63af2545331d0ae899f6faae6588af086b92",
|
|
32
|
+
"releaseUrl": "https://github.com/f5-sales-demo/xcsh/releases/tag/v22.7.2"
|
|
33
33
|
};
|
|
@@ -802,6 +802,10 @@ export class AgentSession {
|
|
|
802
802
|
this.#syncRoutingStateFromBranch();
|
|
803
803
|
this.#ttsrManager = config.ttsrManager;
|
|
804
804
|
this.#obfuscator = config.obfuscator;
|
|
805
|
+
this.agent.setAssistantCheckpointHandler(async message => {
|
|
806
|
+
this.sessionManager.appendAssistantCheckpoint(message);
|
|
807
|
+
await this.sessionManager.flush();
|
|
808
|
+
});
|
|
805
809
|
this.agent.setAssistantMessageEventInterceptor((message, assistantMessageEvent) => {
|
|
806
810
|
const event: AgentEvent = {
|
|
807
811
|
type: "message_update",
|
|
@@ -1363,7 +1367,13 @@ export class AgentSession {
|
|
|
1363
1367
|
// run during tool execution, which happens between message_end and turn_end.
|
|
1364
1368
|
if (event.type === "turn_end" && this.#toolChoiceQueue.hasInFlight) {
|
|
1365
1369
|
const msg = event.message as AssistantMessage;
|
|
1366
|
-
if (
|
|
1370
|
+
if (
|
|
1371
|
+
(msg.stopReason === "aborted" || msg.stopReason === "error") &&
|
|
1372
|
+
!(
|
|
1373
|
+
event.toolChoiceServed ||
|
|
1374
|
+
(msg.interruption && (event.toolResults.length > 0 || msg.interruption.toolChoiceServed))
|
|
1375
|
+
)
|
|
1376
|
+
) {
|
|
1367
1377
|
this.#toolChoiceQueue.reject(msg.stopReason === "error" ? "error" : "aborted");
|
|
1368
1378
|
} else {
|
|
1369
1379
|
this.#toolChoiceQueue.resolve();
|
|
@@ -6723,7 +6733,7 @@ export class AgentSession {
|
|
|
6723
6733
|
* Usage-limit errors are retryable because the retry handler performs credential switching.
|
|
6724
6734
|
*/
|
|
6725
6735
|
#isRetryableError(message: AssistantMessage): boolean {
|
|
6726
|
-
if (message.stopReason !== "error" || !message.errorMessage) return false;
|
|
6736
|
+
if (message.interruption || message.stopReason !== "error" || !message.errorMessage) return false;
|
|
6727
6737
|
|
|
6728
6738
|
// Context overflow is handled by compaction, not retry
|
|
6729
6739
|
const contextWindow = this.model ? getModelEffectiveContextWindow(this.model) : 0;
|
package/src/session/messages.ts
CHANGED
|
@@ -218,6 +218,8 @@ export function createCompactionSummaryMessage(
|
|
|
218
218
|
}
|
|
219
219
|
|
|
220
220
|
export function sanitizeRehydratedOpenAIResponsesAssistantMessage(message: AssistantMessage): AssistantMessage {
|
|
221
|
+
// Codex completed native items support full-history replay after reconnect and reload.
|
|
222
|
+
if (message.api === "openai-codex-responses" && message.interruption) return message;
|
|
221
223
|
if (message.providerPayload?.type !== "openaiResponsesHistory") {
|
|
222
224
|
return message;
|
|
223
225
|
}
|
|
@@ -3,6 +3,7 @@ import * as os from "node:os";
|
|
|
3
3
|
import * as path from "node:path";
|
|
4
4
|
import type { AgentMessage } from "@f5-sales-demo/pi-agent-core";
|
|
5
5
|
import type {
|
|
6
|
+
AssistantMessage,
|
|
6
7
|
ImageContent,
|
|
7
8
|
Message,
|
|
8
9
|
MessageAttribution,
|
|
@@ -238,7 +239,13 @@ export interface CustomMessageEntry<T = unknown> extends SessionEntryBase {
|
|
|
238
239
|
}
|
|
239
240
|
|
|
240
241
|
/** Session entry - has id/parentId for tree structure (returned by "read" methods in SessionManager) */
|
|
242
|
+
export interface AssistantCheckpointEntry extends SessionEntryBase {
|
|
243
|
+
type: "assistant_checkpoint";
|
|
244
|
+
message: AssistantMessage;
|
|
245
|
+
}
|
|
246
|
+
|
|
241
247
|
export type SessionEntry =
|
|
248
|
+
| AssistantCheckpointEntry
|
|
242
249
|
| SessionMessageEntry
|
|
243
250
|
| ThinkingLevelChangeEntry
|
|
244
251
|
| ModelChangeEntry
|
|
@@ -670,9 +677,28 @@ export function buildSessionContext(
|
|
|
670
677
|
// 3. Emit messages after compaction
|
|
671
678
|
const messages: AgentMessage[] = [];
|
|
672
679
|
|
|
680
|
+
const checkpointTimestamps = new Set(
|
|
681
|
+
path.filter(e => e.type === "assistant_checkpoint").map(e => (e as AssistantCheckpointEntry).message.timestamp),
|
|
682
|
+
);
|
|
673
683
|
const appendMessage = (entry: SessionEntry) => {
|
|
674
|
-
if (entry.type === "message") {
|
|
675
|
-
|
|
684
|
+
if (entry.type === "message" || entry.type === "assistant_checkpoint") {
|
|
685
|
+
const message = entry.message;
|
|
686
|
+
if (message.role === "assistant") {
|
|
687
|
+
const retained = message.interruption
|
|
688
|
+
? {
|
|
689
|
+
...message,
|
|
690
|
+
content: message.content.filter((_c, i) =>
|
|
691
|
+
message.interruption!.completedContentIndices.includes(i),
|
|
692
|
+
),
|
|
693
|
+
}
|
|
694
|
+
: message;
|
|
695
|
+
const previous =
|
|
696
|
+
checkpointTimestamps.has(message.timestamp) || message.interruption
|
|
697
|
+
? messages.findIndex(m => m.role === "assistant" && m.timestamp === message.timestamp)
|
|
698
|
+
: -1;
|
|
699
|
+
if (previous >= 0) messages[previous] = retained;
|
|
700
|
+
else messages.push(retained);
|
|
701
|
+
} else messages.push(message);
|
|
676
702
|
} else if (entry.type === "custom_message") {
|
|
677
703
|
messages.push(
|
|
678
704
|
createCustomMessage(
|
|
@@ -1859,7 +1885,9 @@ export class SessionManager {
|
|
|
1859
1885
|
// hadSessionFile: file existed before move → must rewrite to update cwd
|
|
1860
1886
|
// hasAssistant: assistant messages in memory but file missing → recreate from memory
|
|
1861
1887
|
// Neither true → fresh session, never written → preserve lazy-persist
|
|
1862
|
-
const hasAssistant = this.#fileEntries.some(
|
|
1888
|
+
const hasAssistant = this.#fileEntries.some(
|
|
1889
|
+
e => (e.type === "message" || e.type === "assistant_checkpoint") && e.message.role === "assistant",
|
|
1890
|
+
);
|
|
1863
1891
|
if (this.persist && this.#sessionFile && (hadSessionFile || hasAssistant)) {
|
|
1864
1892
|
await this.#rewriteFile();
|
|
1865
1893
|
}
|
|
@@ -2355,7 +2383,9 @@ export class SessionManager {
|
|
|
2355
2383
|
// creating files for sessions that never produce output. Once ensureOnDisk() has
|
|
2356
2384
|
// been called, the session is already on disk and every entry must be flushed.
|
|
2357
2385
|
if (!this.#ensuredOnDisk) {
|
|
2358
|
-
const hasAssistant = this.#fileEntries.some(
|
|
2386
|
+
const hasAssistant = this.#fileEntries.some(
|
|
2387
|
+
e => (e.type === "message" || e.type === "assistant_checkpoint") && e.message.role === "assistant",
|
|
2388
|
+
);
|
|
2359
2389
|
if (!hasAssistant) {
|
|
2360
2390
|
// Mark as not flushed so when assistant arrives, all entries get written.
|
|
2361
2391
|
this.#flushed = false;
|
|
@@ -2411,6 +2441,18 @@ export class SessionManager {
|
|
|
2411
2441
|
* so it is easier to find them.
|
|
2412
2442
|
* These need to be appended via appendCompaction() and appendBranchSummary() methods.
|
|
2413
2443
|
*/
|
|
2444
|
+
appendAssistantCheckpoint(message: AssistantMessage): string {
|
|
2445
|
+
const entry: AssistantCheckpointEntry = {
|
|
2446
|
+
type: "assistant_checkpoint",
|
|
2447
|
+
id: generateId(this.#byId),
|
|
2448
|
+
parentId: this.#leafId,
|
|
2449
|
+
timestamp: new Date().toISOString(),
|
|
2450
|
+
message: structuredClone(message),
|
|
2451
|
+
};
|
|
2452
|
+
this.#appendEntry(entry);
|
|
2453
|
+
return entry.id;
|
|
2454
|
+
}
|
|
2455
|
+
|
|
2414
2456
|
appendMessage(
|
|
2415
2457
|
message:
|
|
2416
2458
|
| Message
|