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
37 changes: 37 additions & 0 deletions src/core/agent.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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<AgentConfig>;
onCorrupted?: (agentId: string, error: ResumeError) => Promise<void> | void;
}
): Promise<Agent> {
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) {
Expand Down
3 changes: 2 additions & 1 deletion src/infra/store/json-store.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
Expand Down
46 changes: 46 additions & 0 deletions tests/unit/infra/json-store.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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() {
Expand Down