diff --git a/src/core/agent.ts b/src/core/agent.ts index f946749..f0e6c9a 100644 --- a/src/core/agent.ts +++ b/src/core/agent.ts @@ -877,6 +877,43 @@ export class Agent { return Agent.resume(agentId, { ...baseConfig, ...overrides }, deps, opts); } + static async resumeOrCreate( + agentId: string, + config: AgentConfig, + deps: AgentDependencies, + opts?: { + autoRun?: boolean; + strategy?: ResumeStrategy; + overrides?: Partial; + onCorrupted?: (agentId: string, error: ResumeError) => Promise | void; + } + ): Promise { + try { + return await Agent.resumeFromStore(agentId, deps, opts); + } catch (error) { + if (!(error instanceof ResumeError)) { + throw error; + } + + switch (error.code) { + case 'AGENT_NOT_FOUND': + return Agent.create({ ...config, agentId }, deps); + + case 'CORRUPTED_DATA': { + if (opts?.onCorrupted) { + await opts.onCorrupted(agentId, error); + } + const store = Agent.requireStore(deps); + await store.delete(agentId); + return Agent.create({ ...config, agentId }, deps); + } + + default: + throw error; + } + } + } + private ensureProcessing() { // 检查是否超时 if (this.processingPromise) { diff --git a/src/infra/store/json-store.ts b/src/infra/store/json-store.ts index 1179945..8e570c3 100644 --- a/src/infra/store/json-store.ts +++ b/src/infra/store/json-store.ts @@ -673,7 +673,8 @@ export class JSONStore implements Store { const fs = require('fs').promises; try { await fs.access(this.getAgentDir(agentId)); - return true; + const info = await this.loadInfo(agentId); + return !!(info && info.metadata); } catch { return false; } diff --git a/tests/unit/infra/json-store.test.ts b/tests/unit/infra/json-store.test.ts index bbaf787..f940c8c 100644 --- a/tests/unit/infra/json-store.test.ts +++ b/tests/unit/infra/json-store.test.ts @@ -142,6 +142,52 @@ runner await store.delete('agent'); expect.toEqual(await store.exists('agent'), false); + }) + + .test('exists() returns false when directory exists but meta.json is missing', async () => { + const dir = createDir('exists-no-meta'); + const store = new JSONStore(dir); + // Create agent directory without meta.json + fs.mkdirSync(path.join(dir, 'agent'), { recursive: true }); + expect.toEqual(await store.exists('agent'), false); + }) + + .test('exists() returns false when meta.json is corrupted JSON', async () => { + const dir = createDir('exists-corrupt'); + const store = new JSONStore(dir); + const agentDir = path.join(dir, 'agent'); + fs.mkdirSync(agentDir, { recursive: true }); + fs.writeFileSync(path.join(agentDir, 'meta.json'), '{not valid json!!!', 'utf-8'); + expect.toEqual(await store.exists('agent'), false); + }) + + .test('exists() returns false when meta.json lacks metadata field', async () => { + const dir = createDir('exists-no-metadata'); + const store = new JSONStore(dir); + const agentDir = path.join(dir, 'agent'); + fs.mkdirSync(agentDir, { recursive: true }); + fs.writeFileSync( + path.join(agentDir, 'meta.json'), + JSON.stringify({ agentId: 'agent', templateId: 'tpl' }), + 'utf-8' + ); + expect.toEqual(await store.exists('agent'), false); + }) + + .test('exists() returns true when meta.json is complete', async () => { + const dir = createDir('exists-complete'); + const store = new JSONStore(dir); + await store.saveInfo('agent', { + agentId: 'agent', + templateId: 'tpl', + createdAt: new Date().toISOString(), + lineage: [], + configVersion: 'test', + messageCount: 0, + lastSfpIndex: 0, + metadata: { templateId: 'tpl' }, + }); + expect.toEqual(await store.exists('agent'), true); }); export async function run() {