| 455 | }; |
| 456 | |
| 457 | export function parseSessionFile(sessionFile: string, wsId: string, wsName: string, harness: string, customInstructionsBytes?: number): Session | null { |
| 458 | |
| 459 | let data: SessionFileData; |
| 460 | try { |
| 461 | if (sessionFile.endsWith('.jsonl')) { |
| 462 | const result = reconstructFromJsonl(sessionFile); |
| 463 | if (!result) return null; |
| 464 | data = result as SessionFileData; |
| 465 | } else { |
| 466 | data = JSON.parse(stripImageData(readFile(sessionFile))) as SessionFileData; |
| 467 | } |
| 468 | } catch (e) { |
| 469 | debugCore('parser-vscode', `Cannot read/parse session file ${sessionFile}`, e); |
| 470 | return null; |
| 471 | } |
| 472 | |
| 473 | const creationTs = data.creationDate ?? null; |
| 474 | let lastMsgTs = data.lastMessageDate ?? null; |
| 475 | const requests = (data.requests || []); |
| 476 | |
| 477 | if (lastMsgTs == null && requests.length > 0) { |
| 478 | lastMsgTs = requests[requests.length - 1].timestamp ?? creationTs; |
| 479 | } |
| 480 | |
| 481 | // Extract session-level reasoning effort default from the JSONL inputState. |
| 482 | // This is the configurationSchema default for the selected model at session start. |
| 483 | const sessionEffortDefault = canonicalizeReasoningEffort( |
| 484 | data.inputState?.selectedModel?.metadata?.configurationSchema |
| 485 | ?.properties?.reasoningEffort?.default ?? null |
| 486 | ); |
| 487 | |
| 488 | // Extract session-level mode from inputState.mode.id. |
| 489 | // VS Code stores the actual mode (agent/ask/edit/plan/custom) here, |
| 490 | // while per-request agent.id only distinguishes the extension participant. |
| 491 | const sessionMode = normalizeSessionMode(data.inputState?.mode?.id); |
| 492 | |
| 493 | const parsedRequests = requests.map(r => { |
| 494 | const req = parseRawRequest(r); |
| 495 | // Apply session-level effort default when per-request effort is unknown |
| 496 | if (!req.reasoningEffort && sessionEffortDefault) { |
| 497 | req.reasoningEffort = sessionEffortDefault; |
| 498 | } |
| 499 | // Apply session-level mode as agentMode — it's the definitive source |
| 500 | // for distinguishing agent/ask/plan/edit/custom modes. |
| 501 | // When absent, clear the per-request agent.id (a participant identifier |
| 502 | // like "copilot") so downstream analytics don't misclassify it as a mode. |
| 503 | req.agentMode = sessionMode; |
| 504 | return req; |
| 505 | }); |
| 506 | const hasDevcontainer = detectDevcontainerFromRequests(parsedRequests); |
| 507 | |
| 508 | return createSession({ |
| 509 | sessionId: data.sessionId || path.basename(sessionFile, path.extname(sessionFile)), |
| 510 | workspaceId: wsId, |
| 511 | workspaceName: wsName, |
| 512 | location: data.initialLocation || 'panel', |
| 513 | harness, |
| 514 | creationDate: creationTs, |