Skip to content

Refactor worker sessions to use HTTP server architecture#78

Merged
gricha merged 6 commits intomainfrom
worker-server-architecture
Jan 10, 2026
Merged

Refactor worker sessions to use HTTP server architecture#78
gricha merged 6 commits intomainfrom
worker-server-architecture

Conversation

@gricha
Copy link
Copy Markdown
Owner

@gricha gricha commented Jan 10, 2026

Summary

  • Replace subprocess CLI calls with HTTP server for session discovery
  • Add perry worker serve command that starts HTTP server on port 7392
  • Create SessionIndex class for caching sessions with file watchers
  • Worker server auto-starts when workspace initializes
  • Agent uses worker client to query sessions via container networking

Architecture

This change improves performance by:

  • Caching session data in memory instead of re-parsing files on every request
  • Using file watchers to detect changes instead of full directory scans
  • Using unified code path for host and container session discovery

The worker server is synced to containers (not baked into images) and started on workspace initialization.

Test plan

  • Unit tests for SessionIndex class
  • Integration tests for worker server
  • Verify bun run validate passes

🤖 Generated with Claude Code

Comment on lines +5 to +14
const clientCache = new Map<string, WorkerClient>();

async function getClient(containerName: string): Promise<WorkerClient> {
let client = clientCache.get(containerName);
if (!client) {
client = await createWorkerClient(containerName);
clientCache.set(containerName, client);
}
return client;
}

This comment was marked as outdated.

Comment thread src/workspace/manager.ts
Comment on lines 290 to +296

private async startWorkerServer(containerName: string): Promise<void> {
const WORKER_PORT = 7392;
const ip = await docker.getContainerIp(containerName);
if (!ip) {
console.warn(
`[sync] Could not get container IP for ${containerName}, skipping worker server`

This comment was marked as outdated.

@gricha gricha closed this Jan 10, 2026
@gricha gricha reopened this Jan 10, 2026
gricha and others added 2 commits January 10, 2026 16:51
Replace subprocess CLI calls with an HTTP server for session discovery:

- Add `perry worker serve` command that starts HTTP server on port 7392
- Create SessionIndex class for caching sessions with file watchers
- Worker server auto-starts when workspace initializes
- Agent uses worker client to query sessions via container networking
- Unified code path for host and container session discovery

This improves performance by caching session data and watching for
changes instead of re-parsing files on every request.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
@gricha gricha force-pushed the worker-server-architecture branch from 616fb5e to af2696a Compare January 10, 2026 16:51
Comment on lines +30 to +36
await Promise.all([this.discoverClaudeSessions(), this.discoverOpencodeSessions()]);

this.initialized = true;
}

async refresh(): Promise<void> {
await Promise.all([this.discoverClaudeSessions(), this.discoverOpencodeSessions()]);

This comment was marked as outdated.

@gricha gricha force-pushed the worker-server-architecture branch from af2696a to 6e0dd14 Compare January 10, 2026 16:57
gricha and others added 2 commits January 10, 2026 17:12
- Handle Claude session format where message.content is a string
- Add messageCount to OpenCode session discovery
- Add integration tests for session titles and message counts
- Update test helper to support running as specific user

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Comment thread src/worker/session-index.ts Outdated
Comment on lines +305 to +306
const textContent = entry.message?.content?.find((c) => c.type === 'text');
if (textContent?.text) {

This comment was marked as outdated.

The Claude session format can have message.content as either a string
or an array of content blocks. Updated both the type definition and
the conversion function to handle both cases properly.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Comment on lines +328 to +330
if (entry.type === 'assistant') {
const content = entry.message?.content;
if (!Array.isArray(content)) return null;
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Bug: The convertClaudeEntry function incorrectly drops assistant messages when their content is a string, as it only handles array-based content. This is a regression from the previous parser.
Severity: CRITICAL

🔍 Detailed Analysis

The convertClaudeEntry function, when processing assistant messages, checks if the content is an array and returns null if it is not. However, the type definition for message.content allows it to be a string, and the previous parser implementation handled this case. This regression causes any assistant message with string-based content to be silently dropped from the session history. This results in incomplete message histories being displayed to users, as the messageCount will be out of sync with the messages returned by getMessages().

💡 Suggested Fix

Update the convertClaudeEntry function to handle string content for assistant messages, not just array content. Add a check for typeof content === 'string' and process it accordingly, similar to how user messages are handled. This will prevent assistant messages from being dropped and align the function with its type definitions.

🤖 Prompt for AI Agent
Review the code at the location below. A potential bug has been identified by an AI
agent.
Verify if this is a real issue. If it is, propose a fix; if not, explain why it's not
valid.

Location: src/worker/session-index.ts#L328-L330

Potential issue: The `convertClaudeEntry` function, when processing assistant messages,
checks if the `content` is an array and returns `null` if it is not. However, the type
definition for `message.content` allows it to be a string, and the previous parser
implementation handled this case. This regression causes any assistant message with
string-based content to be silently dropped from the session history. This results in
incomplete message histories being displayed to users, as the `messageCount` will be out
of sync with the messages returned by `getMessages()`.

Did we get this right? 👍 / 👎 to inform future reviews.
Reference ID: 8434832

@gricha gricha merged commit edb7b4a into main Jan 10, 2026
8 checks passed
@gricha gricha deleted the worker-server-architecture branch January 10, 2026 17:30
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant