@@ -30,6 +30,7 @@ import {
3030 consumeExecApprovalFollowupRuntimeHandoff ,
3131 parseExecApprovalFollowupApprovalId ,
3232} from "../../agents/bash-tools.exec-approval-followup-state.js" ;
33+ import { clearAllCliSessions } from "../../agents/cli-session.js" ;
3334import type { AgentCommandOpts } from "../../agents/command/types.js" ;
3435import { isTimeoutError } from "../../agents/failover-error.js" ;
3536import {
@@ -51,7 +52,7 @@ import { resolveAgentTimeoutMs } from "../../agents/timeout.js";
5152import { agentCommandFromIngress } from "../../commands/agent.js" ;
5253import {
5354 evaluateSessionFreshness ,
54- hasTerminalMainSessionTranscriptNewerThanRegistry ,
55+ hasTerminalMainSessionTranscriptNewerThanRegistrySync ,
5556 mergeSessionEntry ,
5657 resolveTerminalMainSessionTranscriptRegistryCheck ,
5758 resolveChannelResetConfig ,
@@ -1636,28 +1637,33 @@ export const agentHandlers: GatewayRequestHandlers = {
16361637 agentId : canonicalSessionAgentId ,
16371638 } )
16381639 : undefined ;
1639- const freshness = entry
1640+ let freshness = entry
16401641 ? evaluateSessionFreshness ( {
16411642 updatedAt : entry . updatedAt ,
16421643 ...lifecycleTimestamps ,
16431644 now,
16441645 policy : resetPolicy ,
16451646 } )
16461647 : undefined ;
1647- let failedSessionTranscriptMissing = false ;
1648- if ( entry ?. status === "failed" && entry . sessionId ?. trim ( ) ) {
1648+ const resolveFailedSessionTranscriptMissingForEntry = (
1649+ candidateEntry : SessionEntry | undefined ,
1650+ ) => {
1651+ if ( candidateEntry ?. status !== "failed" || ! candidateEntry . sessionId ?. trim ( ) ) {
1652+ return false ;
1653+ }
16491654 try {
16501655 const sessionPathOpts = resolveSessionFilePathOptions ( {
16511656 storePath,
16521657 agentId : canonicalSessionAgentId ,
16531658 } ) ;
1654- failedSessionTranscriptMissing = ! existsSync (
1655- resolveSessionFilePath ( entry . sessionId , entry , sessionPathOpts ) ,
1659+ return ! existsSync (
1660+ resolveSessionFilePath ( candidateEntry . sessionId , candidateEntry , sessionPathOpts ) ,
16561661 ) ;
16571662 } catch {
1658- failedSessionTranscriptMissing = true ;
1663+ return true ;
16591664 }
1660- }
1665+ } ;
1666+ const failedSessionTranscriptMissing = resolveFailedSessionTranscriptMissingForEntry ( entry ) ;
16611667 const mainSessionKeyForRequest = resolveAgentMainSessionKey ( {
16621668 cfg : cfgLocal ,
16631669 agentId : canonicalSessionAgentId ,
@@ -1680,7 +1686,7 @@ export const agentHandlers: GatewayRequestHandlers = {
16801686 storePath,
16811687 } ) ;
16821688 const terminalMainTranscriptNewerThanRegistry = terminalMainTranscriptCheck
1683- ? await hasTerminalMainSessionTranscriptNewerThanRegistry ( {
1689+ ? hasTerminalMainSessionTranscriptNewerThanRegistrySync ( {
16841690 entry,
16851691 sessionScope : cfgLocal . session ?. scope ,
16861692 sessionKey : canonicalKey ,
@@ -1694,7 +1700,7 @@ export const agentHandlers: GatewayRequestHandlers = {
16941700 ( freshness ?. fresh ?? false ) &&
16951701 ! failedSessionTranscriptMissing &&
16961702 ! terminalMainTranscriptNewerThanRegistry ;
1697- const usableRequestedSessionId =
1703+ let usableRequestedSessionId =
16981704 requestedSessionId && ( ! entry ?. sessionId || canReuseSession )
16991705 ? requestedSessionId
17001706 : undefined ;
@@ -1705,7 +1711,7 @@ export const agentHandlers: GatewayRequestHandlers = {
17051711 ! entry ||
17061712 ( ! canReuseSession && ! usableRequestedSessionId ) ||
17071713 Boolean ( usableRequestedSessionId && entry ?. sessionId !== usableRequestedSessionId ) ;
1708- const rotatedSessionId = Boolean ( entry ?. sessionId && entry . sessionId !== sessionId ) ;
1714+ let rotatedSessionId = Boolean ( entry ?. sessionId && entry . sessionId !== sessionId ) ;
17091715 const touchInteraction = ! isSystemGatewayRun && ! request . internalEvents ?. length ;
17101716 const sessionAgent = canonicalSessionAgentId ;
17111717 type AgentSessionPatchBuild = {
@@ -1715,6 +1721,10 @@ export const agentHandlers: GatewayRequestHandlers = {
17151721 groupChannel : string | undefined ;
17161722 groupSpace : string | undefined ;
17171723 freshSessionRotatedSinceLoad : boolean ;
1724+ isNewSession : boolean ;
1725+ rotatedSessionId : boolean ;
1726+ usableRequestedSessionId : string | undefined ;
1727+ freshness : typeof freshness ;
17181728 } ;
17191729 const requestDeliveryHint = normalizeDeliveryContext ( {
17201730 channel : request . channel ?. trim ( ) ,
@@ -1807,12 +1817,69 @@ export const agentHandlers: GatewayRequestHandlers = {
18071817 const freshSessionRotatedSinceLoad = Boolean (
18081818 entry ?. sessionId && freshEntry ?. sessionId && freshEntry . sessionId !== entry . sessionId ,
18091819 ) ;
1810- const patchSessionId = freshSessionRotatedSinceLoad ? freshEntry ?. sessionId : sessionId ;
1811- const shouldClearRotatedState = rotatedSessionId && ! freshSessionRotatedSinceLoad ;
1820+ const freshLifecycleTimestamps = freshEntry
1821+ ? resolveSessionLifecycleTimestamps ( {
1822+ entry : freshEntry ,
1823+ storePath,
1824+ agentId : sessionAgent ,
1825+ } )
1826+ : undefined ;
1827+ const freshFreshness = freshEntry
1828+ ? evaluateSessionFreshness ( {
1829+ updatedAt : freshEntry . updatedAt ,
1830+ ...freshLifecycleTimestamps ,
1831+ now,
1832+ policy : resetPolicy ,
1833+ } )
1834+ : undefined ;
1835+ const freshRequestedSessionMatchesEntry = Boolean (
1836+ requestedSessionId && freshEntry ?. sessionId ?. trim ( ) === requestedSessionId ,
1837+ ) ;
1838+ const freshTerminalMainTranscriptNewerThanRegistry =
1839+ isSystemGatewayRun || freshRequestedSessionMatchesEntry
1840+ ? false
1841+ : hasTerminalMainSessionTranscriptNewerThanRegistrySync ( {
1842+ entry : freshEntry ,
1843+ sessionScope : cfgLocal . session ?. scope ,
1844+ sessionKey : canonicalKey ,
1845+ agentId : sessionAgent ,
1846+ mainKey : cfgLocal . session ?. mainKey ,
1847+ storePath,
1848+ } ) ;
1849+ const freshFailedSessionTranscriptMissing =
1850+ resolveFailedSessionTranscriptMissingForEntry ( freshEntry ) ;
1851+ const freshCanReuseSession =
1852+ Boolean ( freshEntry ?. sessionId ) &&
1853+ ( freshFreshness ?. fresh ?? false ) &&
1854+ ! freshFailedSessionTranscriptMissing &&
1855+ ! freshTerminalMainTranscriptNewerThanRegistry ;
1856+ const freshUsableRequestedSessionId =
1857+ requestedSessionId && ( ! freshEntry ?. sessionId || freshCanReuseSession )
1858+ ? requestedSessionId
1859+ : undefined ;
1860+ const freshSessionId = freshUsableRequestedSessionId
1861+ ? freshUsableRequestedSessionId
1862+ : ( ( freshCanReuseSession ? freshEntry ?. sessionId : undefined ) ?? sessionId ) ;
1863+ const freshIsNewSession =
1864+ ! freshEntry ||
1865+ ( ! freshCanReuseSession && ! freshUsableRequestedSessionId ) ||
1866+ Boolean (
1867+ freshUsableRequestedSessionId &&
1868+ freshEntry ?. sessionId !== freshUsableRequestedSessionId ,
1869+ ) ;
1870+ const freshRotatedSessionId = Boolean (
1871+ freshEntry ?. sessionId && freshEntry . sessionId !== freshSessionId ,
1872+ ) ;
1873+ const patchSessionId = freshSessionRotatedSinceLoad
1874+ ? freshEntry ?. sessionId
1875+ : freshSessionId ;
1876+ const shouldClearRotatedState = freshRotatedSessionId && ! freshSessionRotatedSinceLoad ;
18121877 const patch : Partial < SessionEntry > = {
18131878 sessionId : patchSessionId ,
18141879 updatedAt : now ,
1815- ...( isNewSession && ! freshSessionRotatedSinceLoad ? { sessionStartedAt : now } : { } ) ,
1880+ ...( freshIsNewSession && ! freshSessionRotatedSinceLoad
1881+ ? { sessionStartedAt : now }
1882+ : { } ) ,
18161883 ...( touchInteraction ? { lastInteractionAt : now } : { } ) ,
18171884 ...( effectiveDeliveryFields . route ? { route : effectiveDeliveryFields . route } : { } ) ,
18181885 ...( effectiveDeliveryFields . deliveryContext
@@ -1846,16 +1913,27 @@ export const agentHandlers: GatewayRequestHandlers = {
18461913 }
18471914 : { } ) ,
18481915 } ;
1916+ if ( shouldClearRotatedState ) {
1917+ clearAllCliSessions ( patch ) ;
1918+ }
18491919 return {
18501920 patch,
18511921 spawnedBy : freshSpawnedBy ,
18521922 groupId : nextGroup . groupId ,
18531923 groupChannel : nextGroup . groupChannel ,
18541924 groupSpace : nextGroup . groupSpace ,
18551925 freshSessionRotatedSinceLoad,
1926+ isNewSession : freshIsNewSession ,
1927+ rotatedSessionId : freshRotatedSessionId ,
1928+ usableRequestedSessionId : freshUsableRequestedSessionId ,
1929+ freshness : freshFreshness ,
18561930 } ;
18571931 } ;
18581932 let patchBuild = buildSessionPatch ( entry ) ;
1933+ isNewSession = patchBuild . isNewSession ;
1934+ rotatedSessionId = patchBuild . rotatedSessionId ;
1935+ usableRequestedSessionId = patchBuild . usableRequestedSessionId ;
1936+ freshness = patchBuild . freshness ;
18591937 sessionEntry = mergeSessionEntry ( entry , patchBuild . patch ) ;
18601938 resolvedSessionId = sessionEntry ?. sessionId ?? sessionId ;
18611939 const canonicalSessionKey = canonicalKey ;
@@ -1961,6 +2039,10 @@ export const agentHandlers: GatewayRequestHandlers = {
19612039 return ;
19622040 }
19632041 }
2042+ isNewSession = patchBuild . isNewSession ;
2043+ rotatedSessionId = patchBuild . rotatedSessionId ;
2044+ usableRequestedSessionId = patchBuild . usableRequestedSessionId ;
2045+ freshness = patchBuild . freshness ;
19642046 spawnedByValue = patchBuild . spawnedBy ;
19652047 resolvedGroupId = patchBuild . groupId ;
19662048 resolvedGroupChannel = patchBuild . groupChannel ;
0 commit comments