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
63 changes: 63 additions & 0 deletions packages/agents/test/session-inspector.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,69 @@ describe("session inspector", () => {
expect(state.phaseStatus).toBe("Thinking: Planning repo exploration strategy");
});

it("only updates opencode phase when thinking details are present", () => {
const now = Date.now();
const state = buildSessionMessageState([
{
timestamp: now,
type: "message.part.updated",
data: {
payload: {
type: "message.part.updated",
properties: {
part: {
id: "tool_1",
sessionID: "ses_1",
type: "tool",
tool: "Read",
state: {
status: "running",
input: { filePath: "/tmp/repo/README.md" },
},
},
},
},
},
},
{
timestamp: now + 1,
type: "message.part.updated",
data: {
payload: {
type: "message.part.updated",
properties: {
part: {
id: "reasoning_1",
sessionID: "ses_1",
type: "reasoning",
text: "Planning response sections",
},
},
},
},
},
{
timestamp: now + 2,
type: "message.part.updated",
data: {
payload: {
type: "message.part.updated",
properties: {
part: {
id: "text_1",
sessionID: "ses_1",
type: "text",
text: "Draft message body",
},
},
},
},
},
], { provider: "opencode" });

expect(state.phaseStatus).toBe("Thinking: Planning response sections");
});

it("renders non-empty preview details from wrapped events", () => {
const startedAt = Date.now();
const state = buildSessionMessageState([
Expand Down
1 change: 1 addition & 0 deletions packages/core/cli.ts
Original file line number Diff line number Diff line change
Expand Up @@ -340,6 +340,7 @@ function showLogs(commandArgs: string[]): void {
async function showStatus(): Promise<void> {
const state = daemonState();
const daemonIsRunning = managerRunning(state);
console.log(`Version: ${CURRENT_VERSION}`);
console.log(`Daemon: ${daemonIsRunning ? "running" : "stopped"}`);
if (state.pendingUpgradeRestart) {
console.log(
Expand Down
1 change: 1 addition & 0 deletions packages/core/runtime/event-stream.ts
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ export async function startEventStreamWatcher(
const existingState = liveParsedState.get(messageKey);
const parsedState = buildSessionMessageState(eventHistory, {
workingDirectory: workingPath,
provider: providerId,
baseState: {
startedAt: request.startedAt,
sessionTitle: existingState?.sessionTitle,
Expand Down
1 change: 1 addition & 0 deletions packages/live-status-harness/renderer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ export function renderStatusesFromRun(meta: HarnessRunMeta, events: HarnessCaptu
const state = buildSessionMessageState(sessionEvents, {
endIndex: index,
workingDirectory: meta.cwd,
provider: meta.provider,
baseState: { startedAt: meta.startedAt },
});
const text = buildStatusMessageByProvider(meta.provider, request, meta.cwd, state, "medium");
Expand Down
1 change: 1 addition & 0 deletions packages/live-status-harness/scripts/generate-report.ts
Original file line number Diff line number Diff line change
Expand Up @@ -258,6 +258,7 @@ async function runProvider(
}),
{
workingDirectory: options.cwd,
provider: meta.provider,
baseState: { startedAt: meta.startedAt },
}
);
Expand Down
54 changes: 42 additions & 12 deletions packages/utils/session-inspector.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import {
type StreamStateMaps,
type StreamToolState,
} from "@/agents/session-state/shared";
import type { AgentProviderId } from "@/shared/agent-provider";

export type SessionEvent = {
timestamp: number;
Expand Down Expand Up @@ -64,6 +65,7 @@ export type SessionStateOptions = {
workingDirectory?: string;
endIndex?: number;
baseState?: Partial<SessionMessageState>;
provider?: AgentProviderId;
};

type ProviderParser = {
Expand Down Expand Up @@ -305,7 +307,31 @@ function applyMessageUpdatedEvent(state: SessionMessageState, eventProps: Record
applyMetadataFromRecord(state, info);
}

function applySessionStatusEvent(state: SessionMessageState, eventProps: Record<string, unknown>): void {
function isOpencodeThinkingStatusWithContent(status: string): boolean {
if (!status.startsWith("Thinking:")) return false;
return status.slice("Thinking:".length).trim().length > 0;
}

function updatePhaseStatus(
state: SessionMessageState,
nextStatus: string | undefined,
provider?: AgentProviderId
): void {
if (!nextStatus) return;
if (provider === "opencode") {
if (isOpencodeThinkingStatusWithContent(nextStatus)) {
state.phaseStatus = nextStatus;
}
return;
}
state.phaseStatus = nextStatus;
}

function applySessionStatusEvent(
state: SessionMessageState,
eventProps: Record<string, unknown>,
provider?: AgentProviderId
): void {
const statusValue = (eventProps as { status?: unknown }).status;
const formattedStatus = formatSessionStatus(statusValue);
if (!formattedStatus) return;
Expand All @@ -317,7 +343,7 @@ function applySessionStatusEvent(state: SessionMessageState, eventProps: Record<
) {
return;
}
state.phaseStatus = formattedStatus;
updatePhaseStatus(state, formattedStatus, provider);
}

function normalizeReasoningStatus(text: string): string {
Expand All @@ -331,7 +357,11 @@ function normalizeReasoningStatus(text: string): string {
return `Thinking: ${truncated}`;
}

function applyMessagePartUpdatedEvent(state: SessionMessageState, eventProps: Record<string, unknown>): void {
function applyMessagePartUpdatedEvent(
state: SessionMessageState,
eventProps: Record<string, unknown>,
provider?: AgentProviderId
): void {
const part = (eventProps as { part?: Record<string, unknown> }).part;
if (!part) return;
const isSessionScopedPart = typeof part.sessionID === "string";
Expand Down Expand Up @@ -363,35 +393,35 @@ function applyMessagePartUpdatedEvent(state: SessionMessageState, eventProps: Re
}

if (toolInfo.status === "running" || toolInfo.status === "pending") {
state.phaseStatus = `Running tool: ${toolInfo.name}`;
updatePhaseStatus(state, `Running tool: ${toolInfo.name}`, provider);
} else if (toolInfo.status === "completed") {
state.phaseStatus = `Finished tool: ${toolInfo.name}`;
updatePhaseStatus(state, `Finished tool: ${toolInfo.name}`, provider);
} else if (toolInfo.status === "error") {
state.phaseStatus = `Tool failed: ${toolInfo.name}`;
updatePhaseStatus(state, `Tool failed: ${toolInfo.name}`, provider);
}
return;
}

if (part.type === "text" && typeof part.text === "string") {
state.currentText = part.text;
if (isSessionScopedPart) {
state.phaseStatus = "Drafting response";
updatePhaseStatus(state, "Drafting response", provider);
}
return;
}

if (part.type === "reasoning" && typeof part.text === "string") {
state.thinkingText = part.text;
if (isSessionScopedPart) {
state.phaseStatus = normalizeReasoningStatus(part.text);
updatePhaseStatus(state, normalizeReasoningStatus(part.text), provider);
}
return;
}

if (part.type === "thinking" && typeof part.text === "string") {
state.thinkingText = part.text;
if (isSessionScopedPart) {
state.phaseStatus = normalizeReasoningStatus(part.text);
updatePhaseStatus(state, normalizeReasoningStatus(part.text), provider);
}
}
}
Expand Down Expand Up @@ -472,7 +502,7 @@ export function buildSessionMessageState(
events: SessionEvent[],
options: SessionStateOptions = {}
): SessionMessageState {
const { endIndex, baseState } = options;
const { endIndex, baseState, provider } = options;
const startTime = events[0]?.timestamp ?? Date.now();
const state: SessionMessageState = {
sessionTitle: baseState?.sessionTitle,
Expand Down Expand Up @@ -606,11 +636,11 @@ export function buildSessionMessageState(
}

if (type === "session.status") {
applySessionStatusEvent(state, eventProps);
applySessionStatusEvent(state, eventProps, provider);
}

if (type === "message.part.updated") {
applyMessagePartUpdatedEvent(state, eventProps);
applyMessagePartUpdatedEvent(state, eventProps, provider);
}

if (type === "todo.updated") {
Expand Down
2 changes: 2 additions & 0 deletions packages/web-ui/src/lib/session-inspector/IMPreview.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
...buildSessionMessageState(events, {
endIndex: selectedEventIndex,
workingDirectory,
provider,
}),
currentStatus: "Starting",
currentStep: undefined,
Expand All @@ -40,6 +41,7 @@
const finalState = $derived(({
...buildSessionMessageState(events, {
workingDirectory,
provider,
}),
currentStatus: "Completed",
currentStep: undefined,
Expand Down