본문으로 이동

세션 영속성 이벤트 카탈로그

세션의 내구성 이벤트 로그에 나타날 수 있는 모든 이벤트 유형을 다룹니다. 즉, 완전하게 영속화된 SessionEvent 봉투와 병합 확장이 가능한 SessionEventMap의 각 멤버입니다. 이 리포지토리에서는 @deepseek-ai/dsh-session의 소유 어휘와 모든 플러그인 선언이 @deepseek-ai/dsh-session/types로 병합됩니다. 여기에는 소스 JSDoc, 전체 페이로드 선언, 표면 배지, 선언 위치가 포함됩니다. 이는 session.md(표면 순서 및 deriveMessages() 프로젝션), persistence.md(로그를 내구적으로 만드는 방법), 그리고 session.md의 생성된 영역(라이브 버스 연결 — 로그 이벤트는 cordis 이벤트가 아니며, 단일 session/event emit을 통해 리스너에 도달함)을 보완합니다.

이 파일은 소스(scripts/gen-persistence-catalog.ts)에서 GENERATED되며 pnpm run verify-persistence-catalog(doc-sync의 일부)가 최신 상태인지 검증합니다. 직접 편집하지 마세요. 선언 블록은 포함된 인터페이스/모듈이 부여한 들여쓰기만 제거하고 소스 선언 및 중첩된 속성 JSDoc을 유지하며, ts persistence-catalog 펜스를 사용합니다(선언이 소유 모듈의 타입을 참조하므로 doc-typecheck에서 건너뜀). 페이로드의 타입 이름은 해당 타입을 문서화하는 페이지에 연결됩니다. 영속성 로그 카탈로그 Agent Note를 참조하세요.

아래의 봉투 선언은 각 이벤트의 type, 단조 증가하는 seq, epoch-ms time, data, 선택적 ignorable 알 수 없는 유형 건너뛰기 마커 및 조건부 surfaceOp/sourceEventSeqs 필드로 구성됩니다. surfaceSurfaceEventType 멤버를 표시합니다. 이는 LLM 메시지를 생성하고 표면 목록에 포함되는 방식을 선언합니다. log-only 는 그 외 모든 항목을 표시합니다. 즉, 파생 이력에 기여하지 않는 내구성 있고 재생 가능한 레코드입니다. 모든 페이로드는 JSON 직렬화가 가능하며(Session.append에서 강제됨), 전체 형식은 SESSION_FORMAT_VERSION = 0에 고정됩니다. 릴리스 전 단계이므로 호환성이 보장되지 않습니다(버전 방침). 범위: 이 리포지토리의 패키지입니다. 다운스트림 플러그인은 추가 이벤트 유형을 병합할 수 있으며, 이는 구조상 이 카탈로그의 범위 밖입니다.

이벤트 봉투

ts
/** The appendable event-type keys of {@link SessionEventMap}, plugin-merged extensions included. */
export type SessionEventType = keyof SessionEventMap

/**
 * The subset of {@link SessionEventType} values whose events produce LLM
 * messages and are eligible to appear on the ordered surface. Only these
 * event types may carry {@link SurfaceOp} and {@link SessionEvent.sourceEventSeqs}.
 */
export type SurfaceEventType =
  | 'user/message'
  | 'assistant/message'
  | 'tool/result'

/**
 * How a session event entered the ordered surface. Only valid on
 * {@link SurfaceEventType} events.
 *
 * - `'append'`: added to the tail — normal path for user/assistant/tool
 *   messages.
 * - `{ op: 'replace', start, end }`: replaces surface nodes from `start`
 *   (inclusive) through `end` (inclusive) with this node. Both must exist as
 *   surface nodes in the current surface. `start === end` replaces a single
 *   node. The node's {@link SessionEvent.sourceEventSeqs} must include every
 *   shadowed surface node. Used by compaction; any surface-replacing producer
 *   may use it.
 */
export type SurfaceOp =
  | 'append'
  | { op: 'replace'; start: number; end: number }

/**
 * One immutable entry in the session log.
 *
 * A proper discriminated union over `type` (not independent `type`/`data`
 * unions), so `switch (event.type)` narrows `event.data` without casts.
 *
 * The {@link sourceEventSeqs} and {@link surfaceOp} fields are conditional:
 * they only exist on {@link SurfaceEventType} variants (`user/message`,
 * `assistant/message`, `tool/result`).
 * Non-surface events (boundary markers, chunks, usage, errors) never carry
 * surface metadata — the compiler enforces this at `Session.append()`
 * call sites.
 */
export type SessionEvent<T extends SessionEventType = SessionEventType> = {
  [K in SessionEventType]: {
    type: K
    /** Monotonic sequence number within the session. */
    seq: number
    /** Unix epoch milliseconds. */
    time: number
    data: SessionEventMap[K]
    /**
     * Marks an event a reader may safely skip when it does not recognize
     * `type`. Absent means required: a reader meeting an unrecognized type
     * without this marker MUST refuse to reconstruct the session instead of
     * silently dropping the event, because an unrecognized required event may
     * change how the rest of the log is interpreted. A writer sets `true` only
     * on purely informational records whose loss cannot affect reconstruction;
     * defaulting to required means a forgotten marker over-refuses (an
     * inconvenience) rather than silently resuming a gutted session.
     */
    ignorable?: true
  } & (K extends SurfaceEventType ? {
    /**
     * Seq numbers of earlier events that this event cites as sources
     * (e.g. the `assistant/chunk` seqs that built an `assistant/message`,
     * or the surface nodes shadowed by a compaction replace node). An
     * `assistant/message` may carry a present empty array for a known empty
     * provider stream; when the field is absent, the event does not record which
     * earlier events produced the message.
     */
    sourceEventSeqs?: number[]
    /** How this event entered the surface; absent for non-surface events. */
    surfaceOp?: SurfaceOp
  } : object)
}[T]

소스: packages/core/session/src/types.ts:336 · packages/core/session/src/types.ts:343 · packages/core/session/src/types.ts:372 · packages/core/session/src/types.ts:404

이벤트

agent/*

agent/inbox/spliced — 로그 전용

ts
/**
 * One normalized mutation of an agent's durable pending-message lists.
 * Live dispatch precedes projection mutation, so synchronous observers may
 * read the pre-splice inbox to recover the removed messages.
 */
'agent/inbox/spliced': {
  target: InboxTarget
  start: number
  removedCount?: number
  inserted: UserMessage[]
  outcome?: 'canceled'
}

소스: packages/core/agent/src/types.ts:19

agent-preset/*

agent-preset/selected — 로그 전용

ts
/**
 * The session's agent preset was chosen after creation, while the session
 * was still blank. Log-only: it records the composition later turns ran
 * under, so a resumed or forked session rebuilds the same one instead of
 * the header's creation-time value.
 */
'agent-preset/selected': { agentPreset: string }

소스: packages/preset/agent-presets/src/session.ts:26

approval/*

approval/asked — 로그 전용

ts
/**
 * An approval question was put to the answerer chain — log-only audit
 * (like `hook/*`; NOT a surface event, carries no `surfaceOp`). `id` pairs
 * it with the `approval/decided` that always follows; `toolName` is the
 * tool the question is about, `callId` the exact tool call when the asker
 * had one, `reason` the asker's human-readable explanation (e.g. a hook's
 * permission-decision reason).
 */
'approval/asked': {
  id: ApprovalRequestId
  toolName: string
  callId?: CallId
  reason?: string
}

타입: CallId

소스: packages/interaction/user-approval/src/index.ts:44

approval/decided — 로그 전용

ts
/**
 * The outcome of a prior `approval/asked` (same `id`) — log-only audit.
 * Exactly one per ask, appended when the outcome is known: a decision, a
 * cancellation, or the fail-closed `'unavailable'`.
 */
'approval/decided': {
  id: ApprovalRequestId
  outcome: ApprovalOutcome
}

소스: packages/interaction/user-approval/src/index.ts:55

approval/policy — 로그 전용

ts
/**
 * The session's approval policy was switched — log-only, durable,
 * replayable, never in the model transcript (the model learns the policy
 * from the runtime-context snapshot and live switch notices). The LAST
 * such event is the session's override ({@link effectiveApprovalPolicy}).
 * `source: 'delegation'` marks an override seeded into a child; an absent
 * source is a runtime switch.
 */
'approval/policy': {
  policy: ApprovalPolicy
  /** Marks an override seeded into a child at delegation. */
  source?: 'delegation'
}

소스: packages/interaction/user-approval/src/index.ts:67

assistant/*

assistant/chunk — 로그 전용

ts
/** Raw stream chunk — token-level replay fidelity. */
'assistant/chunk': { turn: number; step: number; chunk: StreamChunk }

유형: StreamChunk

소스: packages/core/session/src/types.ts:266

assistant/message — 표면

ts
/**
 * Assembled assistant message for one step (derived history uses this).
 * Carries the step's `usage` when the adapter reported token accounting, so
 * the model output and its accounting travel together (there is no separate
 * usage record). `usage` is absent when the adapter reported none.
 */
'assistant/message': { turn: number; step: number; message: AssistantMessage; usage?: TokenUsage }

유형: TokenUsage

소스: packages/core/session/src/types.ts:273

command/*

command/done — 로그 전용

ts
/**
 * The paired command settled. `kind`/`text` carry the handler's verbatim
 * outcome (a thrown/aborted handler settles as `kind: 'error'` with the
 * rendered failure). A successful command may identify the earlier
 * authoritative domain event for a richer client-computed presentation.
 */
'command/done': {
  commandId: CommandId
  kind: 'success' | 'error'
  text?: string
  sourceEventSeq?: number
}

소스: packages/interaction/commands/src/types.ts:95

command/run — 로그 전용

ts
/**
 * A resolved slash command entered its handler. Log-only (never model
 * surface); paired with `command/done` by `commandId`, mirroring the
 * `tool/call`↔`tool/result` pairing. The payload is structured — `name`
 * and `args` are `parseCommand`'s own split (name and verbatim rawInput,
 * separator whitespace included), so a consumer (a projection unit
 * folding its own command records, a rich command card) never re-parses
 * a line. `args` is absent when the definition sets `recordInput: false`
 * because an authoritative domain event owns the input payload.
 */
'command/run': { commandId: CommandId; name: string; args?: string; source: CommandSource }

소스: packages/interaction/commands/src/types.ts:88

compaction/*

compaction/end — 로그 전용

ts
/**
 * Marks the end of a compaction — log-only, releases the lock. Its owner
 * matches `compaction/start`; `error` records an unsuccessful attempt.
 */
'compaction/end': { compactionId: CompactionId; sourceCommandId?: CommandId; turn: number | null; error?: string }

소스: packages/compaction/compaction/src/types.ts:71

compaction/prune — 로그 전용

ts
/**
 * Shadow price of one model-free prune replacement — log-only, no
 * surfaceOp. The shared shadow-price protocol: a surface `replace` event
 * is priced by the metering event immediately before it (`compaction/summary`
 * for a summarizing compaction, this event for a prune), which states the
 * heuristic token price of the exact replaced range so a pure consumer
 * can subtract it without retaining per-node prices. The replacement MUST
 * be appended synchronously right after this event.
 */
'compaction/prune': {
  /** The replaced range's first and last surface-node seqs (a surface-position span, like {@link CompactionResult.shadowedRange}). */
  shadowedRange: { start: number; end: number }
  /** The seqs of all shadowed surface nodes, in surface order. */
  shadowedSeqs: number[]
  /** Heuristic price of the shadowed content under the token-meter's fixed estimator. */
  shadowedTokenCount: number
}

소스: packages/compaction/compaction/src/types.ts:81

compaction/start — 로그 전용

ts
/**
 * Marks the start of a compaction — log-only, holds the lock until
 * `compaction/end`. A numbered owner is strictly enclosed by that open turn;
 * `null` identifies a standalone manual transaction between turns.
 */
'compaction/start': { compactionId: CompactionId; sourceCommandId?: CommandId; turn: number | null }

소스: packages/compaction/compaction/src/types.ts:23

compaction/summary — 로그 전용

ts
/**
 * Completed summary, its inputs, and its model call facts — log-only, no surfaceOp.
 * The summary content is in `data.summary`; the actual surface replacement
 * is performed by the immediately following `user/message` event that
 * shadows the compacted range. That adjacency is contractual — the
 * shadowed pricing fields are the replacement's shadow price, so a
 * consumer may pair a replacement with the metering event directly
 * before it (`compaction/prune` documents the shared protocol).
 */
'compaction/summary': {
  compactionId: CompactionId
  sourceCommandId?: CommandId
  summary: ContentBlock[]
  shadowedRange: { start: number; end: number }
  shadowedSeqs: number[]
  shadowedTokenCount: number
  /** The provider route that wrote the summary. */
  provider: string
  /**
   * The model that wrote the summary — the summarize call's envelope,
   * reported by the backend that made the call, logged so the one-shot
   * request is reconstructable from log + code and "which model wrote
   * this summary" has a durable answer (the reconstructability Agent Note).
   */
  model: string
  /** The generation cap the summarize call sent, when one applied. */
  maxTokens?: number
  /** Provider-reported token usage for the summarization request, when emitted. */
  usage?: TokenUsage
} & (
  | {
    /** Complete provider output before the backend's safe summary projection. */
    rawOutput: ContentBlock[]
    /** Identifies exactly one call through this context's `ctx.llm.stream()`. */
    llmStreamCall: true
  }
  | {
    /** Optional complete output from an unmarked template, remote, or other summarizer. */
    rawOutput?: ContentBlock[]
    /** An unmarked summary does not identify a call through this context's LLM seam. */
    llmStreamCall?: never
  }
)

유형: ContentBlock · TokenUsage

소스: packages/compaction/compaction/src/types.ts:33

feedback/*

feedback/record — 로그 전용

ts
/**
 * One recorded human remark about this session. Log-only and independent
 * of its trigger; it never enters model context or derived history.
 */
'feedback/record': { text: string }

소스: packages/feedback/command-feedback/src/index.ts:62

goal/*

goal/change — 로그 전용

ts
/**
 * Complete post-mutation goal state or clear tombstone.
 */
'goal/change': GoalChangeMeta

소스: packages/goal/goal/src/domain.ts:66

hook/*

hook/invoked — 로그 전용

ts
/**
 * A hook command was invoked at a hook point — a log-only record (like
 * `compaction/*`; NOT a {@link SurfaceEventType}, carries no `surfaceOp`).
 * `dialect` is the bridge that ran it (`claude`/`codex`), `point`
 * the hook point (`PreToolUse`, `Stop`, …), `matcher` the matcher-group
 * pattern that selected it (absent for match-all), `handlerId` a stable id
 * for the command (so an invoked/result pair correlates). `turn` is the open
 * turn the invocation lives inside.
 */
'hook/invoked': {
  turn: number
  point: string
  dialect: HookDialect
  matcher?: string
  handlerId: string
}

소스: packages/hooks/hook-protocol/src/types.ts:19

hook/result — 로그 전용

ts
/**
 * Log-only outcome paired to `hook/invoked` by `handlerId`. Decision is the
 * parsed permission result, `stop` for `continue:false`, or `pass`; exit code
 * may be absent, stderr is bounded, and duration is wall-clock runtime.
 */
'hook/result': {
  turn: number
  point: string
  handlerId: string
  decision: string
  exitCode?: number
  stderrSummary?: string
  durationMs: number
}

소스: packages/hooks/hook-protocol/src/types.ts:31

llm/*

llm/retry — 로그 전용

ts
/** Durable, non-surface record of one provider-routed retry scheduled after a failed request attempt. */
'llm/retry': LlmRetryEventData

소스: packages/llm/llm-retry/src/types.ts:9

llm/retry-started — 로그 전용

ts
/** Durable transition written after a retry wait succeeds and before the next request attempt starts. */
'llm/retry-started': LlmRetryStartedEventData

소스: packages/llm/llm-retry/src/types.ts:11

permission/*

permission/preset — 로그 전용

ts
/**
 * Records the selected preset as durable, log-only user intent. The knob
 * events follow in the same turn and control execution; this event stays
 * out of the model transcript and lets {@link effectivePermissionPreset}
 * preserve a selection when bundles match.
 */
'permission/preset': { preset: string }

소스: packages/interaction/permission-presets/src/index.ts:50

plan/*

plan/mode — 로그 전용

ts
/**
 * Whether plan mode is in force from this point on: log-only, non-surface,
 * whole-value replace. The last `plan/mode` wins; a log with none folds to
 * inactive through {@link foldPlanMode}.
 */
'plan/mode': { active: boolean }

소스: packages/plan/plan-mode/src/index.ts:53

request/*

request/context — 로그 전용

ts
/**
 * Route metadata for the next request, logged only when the route or capacity
 * changes. It does not participate in request reconstruction or header equality.
 */
'request/context': RequestContext

소스: packages/core/session/src/types.ts:309

request/header — 로그 전용

ts
/**
 * Full header for the next request, appended inside its step before dispatch.
 * It is log-only; the latest snapshot reconstructs the request header.
 */
'request/header': { header: EpochHeader; reason: RequestHeaderReason }

소스: packages/core/session/src/types.ts:304

sandbox/*

sandbox/mode — 로그 전용

ts
/**
 * The session's sandbox mode was switched — log-only (like `approval/*`;
 * NOT a surface event, carries no `surfaceOp`): durable and replayable,
 * never in the model transcript. The LAST such event is the session's
 * override ({@link effectiveSandboxMode}). `source: 'delegation'` marks
 * an override seeded into a child; an absent source is a runtime switch.
 */
'sandbox/mode': {
  mode: SandboxMode
  /** Marks an override seeded into a child at delegation. */
  source?: 'delegation'
}

소스: packages/sandbox/sandbox-policy/src/session-mode.ts:33

schedule/*

schedule/change — 로그 전용

ts
/**
 * Versioned Schedule mutation. The owning package validates the complete
 * session-local transition stream before accepting a candidate event.
 */
'schedule/change': ScheduleChange

유형: ScheduleChange

소스: packages/schedule/schedule/src/types.ts:219

session/*

session/end-seed — 로그 전용

ts
/**
 * Marks the end of a constructor seed. Events before it have smaller seq
 * values and came from the seed (resume, fork, or replay); this lifecycle
 * produced none of them. This log-only event is the durable projection of
 * {@link Session.firstLiveSeq}. Its payload is empty — position and `time`
 * carry the meaning.
 *
 * Locate the LAST one in stored history. A seed already ending in one is not
 * re-marked, so reopening an untouched session does not grow its log per
 * pickup and the event need not be at the current `firstLiveSeq`.
 *
 * `Session`'s constructor is the only legitimate writer. The invariant
 * companion deliberately constrains nothing here, so a plugin appending one
 * would silently classify every live bracket before it as seed history.
 *
 * An owner of a standalone open/close bracket (`compaction/start` …
 * `compaction/end`) reads it because seed history and live work are otherwise
 * byte-identical: an unmatched opening marker before this event belongs to
 * an ended lifecycle, whatever ended it. NOT a liveness signal about other
 * writers — a concurrently live session holds its own boundary elsewhere,
 * so tolerating concurrent writers needs a signal beyond the log.
 */
'session/end-seed': Record<string, never>

소스: packages/core/session/src/types.ts:332

session/title — 로그 전용

ts
/**
 * Latest-wins session title snapshot. Log-only: it never enters the model
 * surface or derived history.
 */
'session/title': SessionTitleEventData

유형: SessionTitleEventData

소스: packages/session/session-title/src/index.ts:100

session/title-llm-request — 로그 전용

ts
/** Log-only pre-dispatch record of one session-title model request. */
'session/title-llm-request': SessionTitleLlmRequestEventData

유형: SessionTitleLlmRequestEventData

소스: packages/session/session-title-llm/src/index.ts:43

step/*

step/end — 로그 전용

ts
/** Closes step `step` of turn `turn`. */
'step/end': { turn: number; step: number }

소스: packages/core/session/src/types.ts:256

step/start — 로그 전용

ts
/** Opens step `step` of turn `turn` — one model call plus the tool executions it requested. */
'step/start': { turn: number; step: number }

소스: packages/core/session/src/types.ts:254

subagent/*

subagent/descriptor — 로그 전용

ts
/**
 * Durable identity and lifecycle mode of a session-backed subagent child,
 * appended once by the establishing provider inside the child's initial
 * turn, before its first request. Continuable records also carry their
 * resumable composition. Log-only: it carries no `surfaceOp`, never enters
 * model history, and survives compaction.
 */
'subagent/descriptor': SubagentDescriptorData

소스: packages/subagent/subagent/src/descriptor.ts:37

todo/*

todo/write — 로그 전용

ts
/** Whole-list snapshot; latest write wins on replay. Log-only UI state; never derived history. */
'todo/write': { todos: TodoItem[] }

유형: TodoItem

소스: packages/core/session/src/types.ts:299

tool/*

tool/call — 로그 전용

ts
/**
 * The model requested one tool invocation: `name` with the raw `arguments`
 * JSON string exactly as the model produced it (unparsed). `callId` pairs the
 * call with its `tool/result`.
 */
'tool/call': { turn: number; step: number; callId: CallId; name: string; arguments: string }

유형: CallId

소스: packages/core/session/src/types.ts:279

tool/code-dispatch — 로그 전용

ts
/**
 * One bridged sub-dispatch SETTLING: the pairing ids (matching the
 * `tool/code-dispatch-start` with the same `subCallId`), the tool `name`
 * with the same JSON-normalized `arguments`, and the sub-call's complete
 * model-facing outcome in `tool/result`'s own vocabulary
 * (`content` + `isError`), so UIs render a sub-call through the exact
 * code path that renders a native call. Every started sub-call settles
 * with exactly one of these (abort included: the aborted pipeline result
 * is an `isError` outcome).
 * Log-only: `deriveMessages()` ignores it, so sub-calls never re-enter
 * model context; persistence and UIs get every call. Appended inside the
 * parent `run_code`'s execution (the bridge drains in-flight dispatches
 * before returning), so its execution-enclosure relation holds by
 * construction.
 */
'tool/code-dispatch': CodeDispatchEventData

소스: packages/core/tools/src/types.ts:56

tool/code-dispatch-start — 로그 전용

ts
/**
 * One sub-dispatch STARTING inside a `run_code` program: the parent
 * `run_code` call id, the deterministic sub-call id (`<parent>:code:<n>`,
 * numbered in submission order), and the tool `name` with its
 * JSON-normalized `arguments` — the exact value dispatched, normalized
 * BEFORE dispatch, so this append can never fail on payload shape.
 * Appended when the scheduler actually starts the call (not at
 * submission), so a start means the tool body pipeline was entered; a
 * call abandoned in the queue logs nothing. Log-only: `deriveMessages()`
 * ignores it; UIs use it for live per-sub-call running state and pair it
 * with `tool/code-dispatch` by `subCallId` (timing = the two events'
 * `time` fields).
 */
'tool/code-dispatch-start': CodeDispatchStartEventData

소스: packages/core/tools/src/types.ts:40

tool/result — 표시

ts
/**
 * A completed tool call's model-facing result, optional internal failure
 * identity, and optional tool-private `meta` presentation payload. `meta` is
 * opaque to the core (the producing tool owns its shape and reads it back in
 * `presentResult`) but MUST be JSON-serializable: `Session.append`
 * runtime-validates all event data with `isJsonValue`, so a non-serializable
 * `meta` is rejected at the source, and the durable log reproduces the
 * identical card on replay. Absent
 * unless the tool attaches one (e.g. `dsh-tool-fs` carries its result-time
 * contextual diff here).
 */
'tool/result': {
  turn: number
  step: number
  message: ToolResultMessage
  error?: { name: string; code: string }
  meta?: JsonValue
}

소스: packages/core/session/src/types.ts:291

tool-workflow/*

tool-workflow/agent-end — 로그 전용

ts
/**
 * Records one member settlement.
 * @param data - run identity, paired member sequence, and outcome.
 */
'tool-workflow/agent-end': ToolWorkflowAgentEndData

소스: packages/workflow/tool-workflow/src/types.ts:57

tool-workflow/agent-start — 로그 전용

ts
/**
 * Records one published workflow member.
 * @param data - run identity, member sequence, display identity, and child Session.
 */
'tool-workflow/agent-start': ToolWorkflowAgentStartData

소스: packages/workflow/tool-workflow/src/types.ts:52

tool-workflow/run-end — 로그 전용

ts
/**
 * Closes one workflow record after cleanup.
 * @param data - stable run identity and terminal reason.
 */
'tool-workflow/run-end': ToolWorkflowRunEndData

소스: packages/workflow/tool-workflow/src/types.ts:62

tool-workflow/run-start — 로그 전용

ts
/**
 * Opens one top-level workflow record.
 * @param data - stable run identity and display name.
 */
'tool-workflow/run-start': ToolWorkflowRunStartData

소스: packages/workflow/tool-workflow/src/types.ts:47

turn/*

turn/end — 로그 전용

ts
/**
 * Closes turn `turn` with the {@link TurnEndReason} that ended it. A turn
 * with no entered step has no `step/start` or `step/end`. The loop does not await a
 * flush at turn boundaries: `dsh-session-checkpoint-policy` owns the
 * per-request durability checkpoint, and consumers that read storage after
 * `whenIdle()` flush themselves. Success commits the turn; rejection is
 * reported live and does not prevent later work.
 */
'turn/end': { turn: number; reason: TurnEndReason }

유형: TurnEndReason

소스: packages/core/session/src/types.ts:252

turn/start — 로그 전용

ts
/**
 * Opens turn `turn` before the loop claims queued input or runs pre-step.
 * Rejection, empty input, cancellation, or failure may close it with no
 * step; otherwise the following identified `user/message` event or batch
 * records the messages entering the step.
 */
'turn/start': { turn: number }

출처: packages/core/session/src/types.ts:243

user/*

user/message — 표면

ts
/**
 * A user-role message on the model-visible surface: a direct human prompt
 * (the queued message claimed for this turn), a synthetic `agent.inject()`
 * context (file-change notices, subdir AGENTS.md, skill content, cron
 * notifications, …), or an entered goal continuation round. All three
 * project their `content` verbatim; `source` tells them apart.
 */
'user/message': UserMessage

출처: packages/core/session/src/types.ts:264

web/*

web/deepseek-search-llm-request — 로그 전용

ts
/** Secret-free auxiliary DeepSeek search request recorded before dispatch. */
'web/deepseek-search-llm-request': DeepSeekSearchLlmRequest

출처: packages/web/web-search-deepseek/src/provider.ts:83