Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/vs/platform/agentHost/node/claude/CONTEXT.md
Original file line number Diff line number Diff line change
Expand Up @@ -814,7 +814,7 @@ system messages as `SystemNotificationResponsePart`:
| `SDKSystemMessage` subtype | Render? | Rationale |
|---|---|---|
| `compact_boundary` | Yes | "Conversation compacted" — context-loss event |
| `notification` (priority ≥ medium) | Yes | Loop-side text notifications |
| `notification` (all priorities) | Yes | Loop-side text notifications. Earlier draft gated on `priority ≥ medium`; relaxed to all priorities pending real-world data on what `priority: 'low'` notifications actually contain. Revisit and re-introduce the gate if `low` notifications turn out to be noise (transcript-only TODO). |
| `api_retry`, `plugin_install`, `auth_status`, `status` | No | Live UI signals; not transcript content |
| `hook_started`, `hook_progress`, `hook_response` | No | Decorate the associated `ToolCall`, don't stand alone |
| anything else | Drop by default | Conservative; opt in subtypes as needs emerge |
Expand Down
42 changes: 30 additions & 12 deletions src/vs/platform/agentHost/node/claude/claudeAgent.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
*--------------------------------------------------------------------------------------------*/

import type { CCAModel } from '@vscode/copilot-api';
import type { Options, PermissionMode, SDKSessionInfo, SDKUserMessage } from '@anthropic-ai/claude-agent-sdk';
import type { Options, PermissionMode, SDKSessionInfo, SDKUserMessage, SessionMessage } from '@anthropic-ai/claude-agent-sdk';
import { SequencerByKey } from '../../../../base/common/async.js';
import { CancellationError } from '../../../../base/common/errors.js';
import { Emitter } from '../../../../base/common/event.js';
Expand All @@ -23,8 +23,9 @@ import { SessionConfigKey } from '../../common/sessionConfigKeys.js';
import { AgentProvider, AgentSession, AgentSignal, GITHUB_COPILOT_PROTECTED_RESOURCE, IAgent, IAgentCreateSessionConfig, IAgentCreateSessionResult, IAgentDescriptor, IAgentMaterializeSessionEvent, IAgentModelInfo, IAgentResolveSessionConfigParams, IAgentSessionConfigCompletionsParams, IAgentSessionMetadata, IAgentSessionProjectInfo } from '../../common/agentService.js';
import type { ResolveSessionConfigResult, SessionConfigCompletionsResult } from '../../common/state/protocol/commands.js';
import { AHP_AUTH_REQUIRED, ProtocolError } from '../../common/state/sessionProtocol.js';
import { mapSessionMessagesToTurns } from './claudeReplayMapper.js';
import { PolicyState, ProtectedResourceMetadata, type ModelSelection, type ToolDefinition } from '../../common/state/protocol/state.js';
import { CustomizationRef, SessionInputResponseKind, type MessageAttachment, type PendingMessage, type SessionInputAnswer, type ToolCallResult, type Turn } from '../../common/state/sessionState.js';
import { CustomizationRef, isSubagentSession, SessionInputResponseKind, type MessageAttachment, type PendingMessage, type SessionInputAnswer, type ToolCallResult, type Turn } from '../../common/state/sessionState.js';
import { IAgentConfigurationService } from '../agentConfigurationService.js';
import { IAgentHostGitService } from '../agentHostGitService.js';
import { projectFromCopilotContext } from '../copilot/copilotGitProject.js';
Expand Down Expand Up @@ -598,17 +599,34 @@ export class ClaudeAgent extends Disposable implements IAgent {
}

/**
* Full transcript reconstruction from the SDK event log lands in
* Phase 13; the bare method shape is required by {@link IAgent}.
* Phase 13 — reconstruct the full turn history from the SDK's on-disk
* JSONL transcript. Out-of-process: no live `Query` required. Subagent
* URIs (`<parent>/subagent/<toolCallId>`) throw `TODO: Phase 12` until
* Phase 12 wires `getSubagentMessages`. Provisional sessions return `[]`.
* Resilient: any failure (transcript fetch, mapping, backfill) warn-logs
* and returns `[]` rather than propagating — mirrors `listSessions`.
*/
getSessionMessages(_session: URI): Promise<readonly Turn[]> {
// Phase 5 has nothing to reconstruct: there is no SDK Query
// running yet and no event log on disk has been read. The agent
// service surfaces in-memory provisional turns until Phase 13
// implements transcript reconstruction from the SDK event log.
// A fresh array per call avoids leaking mutations across
// subscribers.
return Promise.resolve([]);
async getSessionMessages(session: URI): Promise<readonly Turn[]> {
if (isSubagentSession(session)) {
throw new Error('TODO: Phase 12: subagent transcript fetch not yet implemented');
}
const sessionId = AgentSession.id(session);
if (this._provisionalSessions.has(sessionId)) {
return [];
}
let transcript: readonly SessionMessage[];
try {
transcript = await this._sdkService.getSessionMessages(sessionId, { includeSystemMessages: true });
} catch (err) {
this._logService.warn(`[Claude] getSessionMessages SDK fetch failed for ${sessionId}`, err);
return [];
}
try {
return mapSessionMessagesToTurns(transcript, session, this._logService);
} catch (err) {
this._logService.warn(`[Claude] replay mapper threw for ${sessionId}`, err);
return [];
}
}

async listSessions(): Promise<IAgentSessionMetadata[]> {
Expand Down
17 changes: 16 additions & 1 deletion src/vs/platform/agentHost/node/claude/claudeAgentSdkService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/

import type { ListSessionsOptions, Options, SDKSessionInfo, WarmQuery } from '@anthropic-ai/claude-agent-sdk';
import type { GetSessionMessagesOptions, ListSessionsOptions, Options, SDKSessionInfo, SessionMessage, WarmQuery } from '@anthropic-ai/claude-agent-sdk';
import * as fs from 'fs';
import { pathToFileURL } from 'url';
import { join, resolve } from '../../../../base/common/path.js';
Expand Down Expand Up @@ -59,6 +59,15 @@ export interface IClaudeAgentSdkService {
* can atomically dispatch the deferred `sessionAdded` notification.
*/
startup(params: { options: Options; initializeTimeoutMs?: number }): Promise<WarmQuery>;

/**
* Reads a session's full transcript from disk via the SDK. Out-of-process:
* no live `Query` is required — the SDK parses the JSONL file directly.
* Phase 13 calls this from {@link import('./claudeAgent.js').ClaudeAgent.getSessionMessages}
* with `{ includeSystemMessages: true }` so `compact_boundary` and other
* allowlisted system subtypes survive into the replay mapper.
*/
getSessionMessages(sessionId: string, options?: GetSessionMessagesOptions): Promise<readonly SessionMessage[]>;
}

/**
Expand All @@ -73,6 +82,7 @@ export interface IClaudeSdkBindings {
listSessions(options?: ListSessionsOptions): Promise<SDKSessionInfo[]>;
getSessionInfo(sessionId: string): Promise<SDKSessionInfo | undefined>;
startup(params: { options: Options; initializeTimeoutMs?: number }): Promise<WarmQuery>;
getSessionMessages(sessionId: string, options?: GetSessionMessagesOptions): Promise<SessionMessage[]>;
}

/**
Expand Down Expand Up @@ -122,6 +132,11 @@ export class ClaudeAgentSdkService implements IClaudeAgentSdkService {
return sdk.startup(params);
}

async getSessionMessages(sessionId: string, options?: GetSessionMessagesOptions): Promise<readonly SessionMessage[]> {
const sdk = await this._getSdk();
return sdk.getSessionMessages(sessionId, options);
}

private async _getSdk(): Promise<IClaudeSdkBindings> {
if (this._sdkModule) {
return this._sdkModule;
Expand Down
Loading
Loading