Persist conversation history to disk + mount workers under gateway appdata
- TS worker saves/loads messages to {stateRoot}/conversation.json
- Saves after user message, assistant response, and session reset
- Loads on engine construction (survives container restarts)
- Add CLAW_GATEWAY_WORKER_HOST_STATE_ROOT and
CLAW_GATEWAY_WORKER_HOST_WORKSPACE_ROOT to Unraid XML, defaulting
to /mnt/user/appdata/claw-telegram-gateway/workers
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -33,6 +33,8 @@
|
||||
<Config Name="Template Dir" Target="CLAW_GATEWAY_TEMPLATE_DIR" Default="/unraid/templates-user" Mode="" Description="Unraid template directory path inside the gateway container" Type="Variable" Display="advanced" Required="true" Mask="false">/unraid/templates-user</Config>
|
||||
<Config Name="Template Archive Dir" Target="CLAW_GATEWAY_TEMPLATE_ARCHIVE_DIR" Default="/appdata/template-archive" Mode="" Description="Archive location for removed worker templates" Type="Variable" Display="advanced" Required="true" Mask="false">/appdata/template-archive</Config>
|
||||
<Config Name="Worker Network" Target="CLAW_GATEWAY_WORKER_NETWORK" Default="claw_gateway" Mode="" Description="Docker network name shared by the gateway and workers" Type="Variable" Display="advanced" Required="true" Mask="false">claw_gateway</Config>
|
||||
<Config Name="Worker Host State Root" Target="CLAW_GATEWAY_WORKER_HOST_STATE_ROOT" Default="/mnt/user/appdata/claw-telegram-gateway/workers" Mode="" Description="Host path root for worker state directories (bind-mounted into worker containers)" Type="Variable" Display="advanced" Required="true" Mask="false">/mnt/user/appdata/claw-telegram-gateway/workers</Config>
|
||||
<Config Name="Worker Host Workspace Root" Target="CLAW_GATEWAY_WORKER_HOST_WORKSPACE_ROOT" Default="/mnt/user/appdata/claw-telegram-gateway/workers" Mode="" Description="Host path root for worker workspace directories (bind-mounted into worker containers)" Type="Variable" Display="advanced" Required="true" Mask="false">/mnt/user/appdata/claw-telegram-gateway/workers</Config>
|
||||
<Config Name="Inherited Env" Target="CLAW_GATEWAY_INHERITED_ENV" Default="ANTHROPIC_AUTH_TOKEN,ANTHROPIC_API_KEY" Mode="" Description="Comma-separated env vars copied from gateway into worker containers" Type="Variable" Display="advanced" Required="false" Mask="false">ANTHROPIC_AUTH_TOKEN,ANTHROPIC_API_KEY</Config>
|
||||
<Config Name="Worker Ready Timeout (s)" Target="CLAW_GATEWAY_WORKER_READY_TIMEOUT_SECS" Default="15" Mode="" Description="Seconds to wait for a worker container to pass its health check" Type="Variable" Display="advanced" Required="false" Mask="false">15</Config>
|
||||
<Config Name="Anthropic OAuth Token" Target="ANTHROPIC_AUTH_TOKEN" Default="" Mode="" Description="Claude subscription OAuth token (sk-ant-oat...) inherited by worker containers" Type="Variable" Display="always" Required="false" Mask="true"></Config>
|
||||
|
||||
@@ -14,6 +14,8 @@
|
||||
*/
|
||||
|
||||
import Anthropic from '@anthropic-ai/sdk'
|
||||
import { readFileSync, writeFileSync, mkdirSync } from 'node:fs'
|
||||
import { join, dirname } from 'node:path'
|
||||
import type { ApprovalBroker } from '../permissions/ApprovalBroker.js'
|
||||
import { WorkerEventTranslator } from '../events/WorkerEventTranslator.js'
|
||||
import type { WorkerTurnEvent } from '../protocol.js'
|
||||
@@ -47,7 +49,7 @@ export class TelegramWorkerEngine {
|
||||
private client: Anthropic | null = null
|
||||
private useOAuth = false
|
||||
|
||||
// Session state — persists across turns
|
||||
// Session state — persists across turns and container restarts
|
||||
private messages: ConversationMessage[] = []
|
||||
private ready = false
|
||||
|
||||
@@ -55,6 +57,30 @@ export class TelegramWorkerEngine {
|
||||
this.config = config
|
||||
this.broker = broker
|
||||
this.translator = new WorkerEventTranslator()
|
||||
this.loadMessages()
|
||||
}
|
||||
|
||||
private get messagesPath(): string {
|
||||
return join(this.config.stateRoot, 'conversation.json')
|
||||
}
|
||||
|
||||
private loadMessages(): void {
|
||||
try {
|
||||
const data = readFileSync(this.messagesPath, 'utf-8')
|
||||
this.messages = JSON.parse(data)
|
||||
console.log(`[TelegramWorkerEngine] loaded ${this.messages.length} messages from disk`)
|
||||
} catch {
|
||||
this.messages = []
|
||||
}
|
||||
}
|
||||
|
||||
private saveMessages(): void {
|
||||
try {
|
||||
mkdirSync(dirname(this.messagesPath), { recursive: true })
|
||||
writeFileSync(this.messagesPath, JSON.stringify(this.messages), 'utf-8')
|
||||
} catch (err) {
|
||||
console.error('[TelegramWorkerEngine] failed to save messages:', err)
|
||||
}
|
||||
}
|
||||
|
||||
async init(): Promise<boolean> {
|
||||
@@ -112,6 +138,7 @@ export class TelegramWorkerEngine {
|
||||
|
||||
// Add user message to history
|
||||
this.messages.push({ role: 'user', content: prompt })
|
||||
this.saveMessages()
|
||||
|
||||
try {
|
||||
const agentPrompt = `You are Claude, a personal AI assistant communicating via Telegram. You have no persistent memory between sessions — every conversation could be your last with this person before a reset wipes everything you've learned.
|
||||
@@ -181,6 +208,7 @@ Current working directory: ${this.config.defaultCwd}`
|
||||
.join('') || fullText
|
||||
|
||||
this.messages.push({ role: 'assistant', content: assistantText })
|
||||
this.saveMessages()
|
||||
|
||||
yield {
|
||||
type: 'completed',
|
||||
@@ -199,6 +227,7 @@ Current working directory: ${this.config.defaultCwd}`
|
||||
/** Reset session — clear conversation history. */
|
||||
resetSession(): void {
|
||||
this.messages = []
|
||||
this.saveMessages()
|
||||
this.broker.reset()
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user