Files
nanoclaw/container/agent-runner/src/providers/mock.ts
gavrielc 6f2a7314d0 v2: fix agent-runner lifecycle and session DB reliability
- Use DELETE journal mode for session DBs instead of WAL. WAL doesn't
  sync reliably across Docker volume mounts (VirtioFS), causing dropped
  writes and duplicate deliveries.
- Add 20s idle detection to end the query stream. The concurrent poll
  tracks SDK activity via a new 'activity' provider event. When no SDK
  events arrive for 20s and no messages are pending, the stream ends
  and the poll loop continues.
- Add touchProcessing heartbeat so the host can distinguish active
  agents from idle ones by checking status_changed recency.
- Catch query errors in the poll loop and write error responses to
  messages_out instead of crashing the process.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-09 01:34:59 +03:00

69 lines
1.9 KiB
TypeScript

import type { AgentProvider, AgentQuery, ProviderEvent, QueryInput } from './types.js';
/**
* Mock provider for testing. Returns canned responses.
* Supports push() — queued messages produce additional results.
*/
export class MockProvider implements AgentProvider {
private responseFactory: (prompt: string) => string;
constructor(responseFactory?: (prompt: string) => string) {
this.responseFactory = responseFactory ?? ((prompt) => `Mock response to: ${prompt.slice(0, 100)}`);
}
query(input: QueryInput): AgentQuery {
const pending: string[] = [];
let waiting: (() => void) | null = null;
let ended = false;
let aborted = false;
const responseFactory = this.responseFactory;
const events: AsyncIterable<ProviderEvent> = {
async *[Symbol.asyncIterator]() {
yield { type: 'activity' };
yield { type: 'init', sessionId: `mock-session-${Date.now()}` };
// Process initial prompt
yield { type: 'activity' };
yield { type: 'result', text: responseFactory(input.prompt) };
// Process any pushed follow-ups
while (!ended && !aborted) {
if (pending.length > 0) {
const msg = pending.shift()!;
yield { type: 'result', text: responseFactory(msg) };
continue;
}
// Wait for push() or end()
await new Promise<void>((resolve) => {
waiting = resolve;
});
waiting = null;
}
// Drain remaining
while (pending.length > 0) {
const msg = pending.shift()!;
yield { type: 'result', text: responseFactory(msg) };
}
},
};
return {
push(message: string) {
pending.push(message);
waiting?.();
},
end() {
ended = true;
waiting?.();
},
events,
abort() {
aborted = true;
waiting?.();
},
};
}
}