diff --git a/packages/core/src/agent.ts b/packages/core/src/agent.ts index 23a4e80..fe642c7 100644 --- a/packages/core/src/agent.ts +++ b/packages/core/src/agent.ts @@ -70,26 +70,6 @@ import type { ModelEntry } from "./state/index.js"; */ const MAX_SUBAGENT_DEPTH = 1; -/** The five valid thinking level names (legacy session_meta additionally recorded the literal "default" for "no level"). */ -const THINKING_LEVEL_NAMES: readonly ThinkingLevelName[] = [ - "none", - "low", - "medium", - "high", - "xhigh", -]; - -/** - * Narrows a legacy session_meta `thinking_level` back to a ThinkingLevelName; the literal - * "default" (no level recorded), a missing field (current Traces no longer record one), and - * anything unknown are not levels and yield undefined. - */ -function asThinkingLevelName(value: unknown): ThinkingLevelName | undefined { - return typeof value === "string" && (THINKING_LEVEL_NAMES as readonly string[]).includes(value) - ? (value as ThinkingLevelName) - : undefined; -} - export interface CreateAgentOptions { agentId?: string; projectId?: string; @@ -394,15 +374,12 @@ export class Agent { // original history); the vault uses current values (it's injected into the // subprocess environment, not the history, so a resumed Session should get the // latest keys too). - // Back-compat: session_meta no longer records a thinking level (it became a per-turn - // run parameter), but OLD Traces still carry `thinking_level` in their meta JSON — when - // present, keep honoring it as this Session's default level (a resumed legacy subagent - // session keeps its inherited level instead of re-reading this Agent's config). The - // field is read loosely (it's gone from SessionMetaPayload); the legacy literal - // "default" — and any current Trace without the field — falls back to the Agent config. - const thinkingLevel = - asThinkingLevelName((meta as unknown as Record).thinking_level) ?? - this.state.systemConfig.model?.thinking_level; + // The default thinking level comes from this Agent's current config only: session_meta + // no longer records one (it became a per-turn run parameter), and a `thinking_level` + // still present in a legacy Trace's meta JSON is deliberately ignored — a resumed + // legacy subagent session falls back to this Agent's configured level instead of + // keeping the level it inherited at spawn time. + const thinkingLevel = this.state.systemConfig.model?.thinking_level; const rt = await this.buildRuntime({ workspaceDir, @@ -442,8 +419,7 @@ export class Agent { }); return new Session({ - // Invariants only (the legacy thinking_level, when honored above, feeds the LLM default - // but is not re-recorded — new meta writes never contain it). + // Invariants only — meta writes never contain a thinking level. meta: { session_id: sessionId, provider: modelEntry.provider, diff --git a/packages/core/src/omnimessage/types.ts b/packages/core/src/omnimessage/types.ts index 2093c53..3fe2cb0 100644 --- a/packages/core/src/omnimessage/types.ts +++ b/packages/core/src/omnimessage/types.ts @@ -83,8 +83,8 @@ export interface ToolDefinition { /** * Session metadata. Holds **per-session invariants only** — values fixed for the Session's * lifetime (model reference, assembled system prompt, tool schemas, paths, origin). Per-turn - * parameters (e.g. the thinking level, passed with each run) never belong here; legacy Traces - * may still carry a `thinking_level` field, which resume reads loosely for back-compat. + * parameters (e.g. the thinking level, passed with each run) never belong here; a + * `thinking_level` field still present in a legacy Trace's meta is ignored on resume. */ export interface SessionMetaPayload { session_id: string; diff --git a/packages/core/test/resume.test.ts b/packages/core/test/resume.test.ts index 54ec2e9..b69abfe 100644 --- a/packages/core/test/resume.test.ts +++ b/packages/core/test/resume.test.ts @@ -175,11 +175,12 @@ describe("agent.resumeSession", () => { ).toEqual(["text", "text", "abort"]); }); - it("honors a legacy trace's recorded thinking_level; new meta never re-records it", async () => { - // session_meta no longer carries a thinking level (it became a per-turn run parameter), - // but OLD traces still have it in their meta JSON: resume must keep honoring it as the - // session's default level (a legacy subagent session keeps its inherited level instead of - // re-reading this Agent's config). The seeded Agent config here pins "medium". + it("ignores a legacy trace's recorded thinking_level; the Agent config always wins", async () => { + // session_meta no longer carries a thinking level (it became a per-turn run parameter); + // a `thinking_level` still present in an OLD trace's meta JSON is deliberately ignored — + // resume always reads this Agent's current config, so a resumed legacy subagent session + // falls back to the config level instead of keeping the level it inherited at spawn + // time. The seeded Agent config here pins "medium". const agent = await createAgent({}); expect(agent.state.systemConfig.model?.thinking_level).toBe("medium"); const levelOf = (session: unknown): unknown => @@ -190,14 +191,14 @@ describe("agent.resumeSession", () => { // A legacy trace: inject the retired field loosely into the on-disk meta JSON. (recorded.payload as unknown as Record).thinking_level = "xhigh"; await writeTraceFile(tmpRoot, SID, [recorded, userText("hello")]); - const inherited = await agent.resumeSession({ sessionId: SID }); - expect(levelOf(inherited)).toBe("xhigh"); - // The rebuilt meta holds invariants only: the legacy field is honored but never re-recorded. + const ignored = await agent.resumeSession({ sessionId: SID }); + expect(levelOf(ignored)).toBe("medium"); + // The rebuilt meta holds invariants only: the legacy field is never re-recorded either. expect( - "thinking_level" in (inherited.metaMessage.payload as unknown as Record), + "thinking_level" in (ignored.metaMessage.payload as unknown as Record), ).toBe(false); - // A current trace (no field) — and the legacy literal "default" — fall back to the Agent config. + // A current trace (no field) — and the legacy literal "default" — read the Agent config too. const SID2 = "session-2026-07-06-11-00-00-abcdef02"; await writeTraceFile(tmpRoot, SID2, [metaFor(SID2, workspace), userText("hi")]); const fallback = await agent.resumeSession({ sessionId: SID2 }); diff --git a/packages/docs/content/omni-message.en.md b/packages/docs/content/omni-message.en.md index df6752d..4ded091 100644 --- a/packages/docs/content/omni-message.en.md +++ b/packages/docs/content/omni-message.en.md @@ -50,7 +50,7 @@ interface ToolDefinition { } ``` -session_meta holds **per-session invariants only** — the model, system prompt and Workspace are immutable for the Session's lifetime; on resume, the engine takes this Trace line as the runtime config. See [Sessions & Traces](/sessions-and-traces). The thinking level is a per-turn parameter (sent with each Task) and is not recorded here; legacy Traces may still carry a `thinking_level` field in their meta, which resume keeps honoring for back-compat. +session_meta holds **per-session invariants only** — the model, system prompt and Workspace are immutable for the Session's lifetime; on resume, the engine takes this Trace line as the runtime config. See [Sessions & Traces](/sessions-and-traces). The thinking level is a per-turn parameter (sent with each Task) and is not recorded here; a `thinking_level` field still present in a legacy Trace's meta is ignored on resume — the resumed Session reads the Agent's current config instead. ## model_msg: complete payloads diff --git a/packages/docs/content/omni-message.zh.md b/packages/docs/content/omni-message.zh.md index d5609da..82c6c8a 100644 --- a/packages/docs/content/omni-message.zh.md +++ b/packages/docs/content/omni-message.zh.md @@ -50,7 +50,7 @@ interface ToolDefinition { } ``` -session_meta 只承载**会话级不变量**——模型、系统提示词、Workspace 在 Session 生命周期内不可变;恢复 Session 时引擎直接以 Trace 中的这条消息为运行时配置,见 [Session 与 Trace](/sessions-and-traces)。思考等级是逐轮参数(随每次 Task 下发),不记录在此;旧版 Trace 的 meta 里可能仍带 `thinking_level` 字段,恢复时按兼容逻辑继续生效。 +session_meta 只承载**会话级不变量**——模型、系统提示词、Workspace 在 Session 生命周期内不可变;恢复 Session 时引擎直接以 Trace 中的这条消息为运行时配置,见 [Session 与 Trace](/sessions-and-traces)。思考等级是逐轮参数(随每次 Task 下发),不记录在此;旧版 Trace 的 meta 里可能仍带 `thinking_level` 字段,恢复时会被忽略——恢复后的 Session 直接读取 Agent 当前配置。 ## model_msg:完整消息