chore(chat): instrumentation session CustomAgentChatView pour diagnostic #149

Ajoute des points de traçage ciblés sur le cycle de vie de session
CustomAgentChatView/LayoutGrid pour isoler la cause du fallback
silencieux CLI custom avant tentative de fix.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-08-05 15:23:45 +02:00
parent 4d8b69afca
commit 029ed97bff
2 changed files with 149 additions and 20 deletions

View File

@ -40,6 +40,19 @@ function describe(e: unknown): string {
return String(e);
}
function instrumentationError(e: unknown): Record<string, unknown> {
if (e && typeof e === "object") {
const err = e as GatewayError & { name?: string };
return {
code: err.code,
message: err.message,
name: err.name,
raw: e,
};
}
return { message: String(e), raw: e };
}
function isNotFound(e: unknown): boolean {
return Boolean(e && typeof e === "object" && (e as GatewayError).code === "NOT_FOUND");
}
@ -127,6 +140,7 @@ export function CustomAgentChatView({
const sessionRef = useRef<string | null>(sessionId);
sessionRef.current = currentSession;
const mountedRef = useRef(false);
const openOrAttachCountRef = useRef(0);
const onSessionIdRef = useRef(onSessionId);
onSessionIdRef.current = onSessionId;
const onConversationIdRef = useRef(onConversationId);
@ -156,7 +170,28 @@ export function CustomAgentChatView({
const reattachStructuredSession = useCallback(
async (sid: string, options: { applyScrollback?: boolean } = {}) => {
if (!agent.reattachAgentChat) throw new Error("Structured reattach unavailable");
const reattached = await agent.reattachAgentChat(sid, receive);
console.debug("[ticket149] reattachStructuredSession:start", {
timestamp: new Date().toISOString(),
sessionId: sid,
options,
});
let reattached: Awaited<ReturnType<NonNullable<typeof agent.reattachAgentChat>>>;
try {
reattached = await agent.reattachAgentChat(sid, receive);
} catch (e) {
console.error("[ticket149] reattachStructuredSession:error", {
timestamp: new Date().toISOString(),
sessionId: sid,
error: instrumentationError(e),
});
throw e;
}
console.debug("[ticket149] reattachStructuredSession:success", {
timestamp: new Date().toISOString(),
requestedSessionId: sid,
returnedSessionId: reattached.sessionId,
scrollbackCount: reattached.scrollback.length,
});
if (!mountedRef.current) return reattached.sessionId;
setCurrentSession(reattached.sessionId);
if (options.applyScrollback) {
@ -170,14 +205,47 @@ export function CustomAgentChatView({
const recoverStructuredSession = useCallback(
async (options: { applyScrollback?: boolean; retryAttachNotFound?: boolean } = {}) => {
if (!agent.launchAgentChat) throw new Error("Structured launch unavailable");
console.debug("[ticket149] recoverStructuredSession:start", {
timestamp: new Date().toISOString(),
currentSession: sessionRef.current,
conversationId,
options,
});
let retryAttachNotFound = Boolean(options.retryAttachNotFound);
for (;;) {
const launched = await agent.launchAgentChat(projectId, agentId, {
const launchRequest = {
cwd,
rows: 24,
cols: 80,
conversationId: conversationId ?? undefined,
nodeId,
};
console.debug("[ticket149] recoverStructuredSession:launchAgentChat:start", {
timestamp: new Date().toISOString(),
projectId,
agentId,
request: launchRequest,
options,
retryAttachNotFound,
});
let launched: Awaited<ReturnType<NonNullable<typeof agent.launchAgentChat>>>;
try {
launched = await agent.launchAgentChat(projectId, agentId, launchRequest);
} catch (e) {
console.error("[ticket149] recoverStructuredSession:launchAgentChat:error", {
timestamp: new Date().toISOString(),
projectId,
agentId,
request: launchRequest,
error: instrumentationError(e),
});
throw e;
}
console.debug("[ticket149] recoverStructuredSession:launchAgentChat:success", {
timestamp: new Date().toISOString(),
projectId,
agentId,
launched,
});
if (!mountedRef.current) return launched.sessionId;
setCurrentSession(launched.sessionId);
@ -186,9 +254,15 @@ export function CustomAgentChatView({
onConversationIdRef.current(launched.assignedConversationId);
}
try {
return await reattachStructuredSession(launched.sessionId, {
const recoveredSessionId = await reattachStructuredSession(launched.sessionId, {
applyScrollback: options.applyScrollback,
});
console.debug("[ticket149] recoverStructuredSession:success", {
timestamp: new Date().toISOString(),
launchedSessionId: launched.sessionId,
recoveredSessionId,
});
return recoveredSessionId;
} catch (e) {
if (isNotFound(e) && retryAttachNotFound) {
retryAttachNotFound = false;
@ -198,6 +272,11 @@ export function CustomAgentChatView({
}
continue;
}
console.error("[ticket149] recoverStructuredSession:reattach:error", {
timestamp: new Date().toISOString(),
launchedSessionId: launched.sessionId,
error: instrumentationError(e),
});
throw e;
}
}
@ -223,6 +302,13 @@ export function CustomAgentChatView({
let cancelled = false;
async function openOrAttach() {
const execution = openOrAttachCountRef.current + 1;
openOrAttachCountRef.current = execution;
console.debug("[ticket149] openOrAttach:start", {
timestamp: new Date().toISOString(),
execution,
sessionId,
});
setOpening(true);
setError(null);
try {
@ -230,6 +316,11 @@ export function CustomAgentChatView({
try {
await reattachStructuredSession(sessionId, { applyScrollback: true });
if (cancelled) return;
console.debug("[ticket149] openOrAttach:reattach-existing:success", {
timestamp: new Date().toISOString(),
execution,
sessionId,
});
return;
} catch (e) {
// The backend contract for `reattach_agent_chat` (NOT_FOUND) is that
@ -240,6 +331,12 @@ export function CustomAgentChatView({
setCurrentSession(null);
onSessionIdRef.current(null);
}
console.debug("[ticket149] openOrAttach:reattach-existing:not-found", {
timestamp: new Date().toISOString(),
execution,
sessionId,
error: instrumentationError(e),
});
}
}
await recoverStructuredSession({
@ -247,7 +344,18 @@ export function CustomAgentChatView({
retryAttachNotFound: true,
});
if (cancelled) return;
console.debug("[ticket149] openOrAttach:recover:success", {
timestamp: new Date().toISOString(),
execution,
receivedSessionId: sessionId,
});
} catch (e) {
console.error("[ticket149] openOrAttach:error", {
timestamp: new Date().toISOString(),
execution,
sessionId,
error: instrumentationError(e),
});
if (!cancelled) setError(describe(e));
} finally {
if (!cancelled) setOpening(false);