From 95dd0a1f3ba763e328b46f5bfada5b2eeb82ee8a Mon Sep 17 00:00:00 2001 From: _Kerman Date: Fri, 5 Jun 2026 14:51:58 +0800 Subject: [PATCH] chore: clarify agent record resume contract (#465) --- packages/agent-core/src/agent/records/index.ts | 18 +++++++++++++----- packages/agent-core/src/agent/records/types.ts | 4 ++++ 2 files changed, 17 insertions(+), 5 deletions(-) diff --git a/packages/agent-core/src/agent/records/index.ts b/packages/agent-core/src/agent/records/index.ts index 6960a362b..65a5ee6bb 100644 --- a/packages/agent-core/src/agent/records/index.ts +++ b/packages/agent-core/src/agent/records/index.ts @@ -19,9 +19,16 @@ export type { FileSystemAgentRecordPersistenceOptions } from './persistence'; export { BlobStore, isBlobRef } from './blobref'; export type { BlobStoreOptions } from './blobref'; -// Contract: restore MUST NOT emit UI events, call the LLM, execute tools, or -// touch the filesystem in a way that triggers external side effects. Each case -// should reproduce the in-memory state the live handler left behind, nothing more. +// Contract: restore MUST only rebuild in-memory state. It must not emit UI +// events, call the LLM, execute tools, start background work, make network +// requests, or touch the filesystem in a way that triggers external side effects. +// +// Prefer restoring by calling the same method that wrote the record, so live +// execution and resume share one state mutation path. For example, +// permission.set_mode replays through agent.permission.setMode(input.mode), +// not by assigning modeOverride here. records.logRecord, emitEvent, and +// emitStatusUpdated already gate on records.restoring, so those calls are safe +// during resume. function restoreAgentRecord(agent: Agent, input: AgentRecord): void { switch (input.type) { case 'metadata': @@ -95,8 +102,9 @@ function restoreAgentRecord(agent: Agent, input: AgentRecord): void { case 'tools.update_store': agent.tools.updateStore(input.key, input.value); return; - // Goal records are an audit trail only. Goal state is restored from - // `state.json` (metadata.custom.goal), never rebuilt from these records. + // TODO: Move goal state transitions to real resume semantics. These records + // are currently audit-only, while goal state is restored from `state.json` + // (metadata.custom.goal) instead of being rebuilt from ordered records. case 'goal.create': case 'goal.update': case 'goal.account_usage': diff --git a/packages/agent-core/src/agent/records/types.ts b/packages/agent-core/src/agent/records/types.ts index 07d7f1e1e..f2b9a783c 100644 --- a/packages/agent-core/src/agent/records/types.ts +++ b/packages/agent-core/src/agent/records/types.ts @@ -10,6 +10,10 @@ import type { PermissionApprovalResultRecord, PermissionMode } from '../permissi import type { UserToolRegistration } from '../tool'; import type { UsageRecordScope } from '../usage'; +// Agent records are the ordered event log used to rebuild agent state on resume. +// Use records, not state.json, when correctness depends on the order in which +// state transitions happened. Each persisted record type must have explicit +// resume semantics in restoreAgentRecord; a write-only record is not persistence. export interface AgentRecordEvents { metadata: { protocol_version: string;