Handoff-style /model switch command and per-turn thinking level (#64)
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
1 parent
816a823b75
commit
1e23452f48
58 files changed
+1064
-336
No files matched your search
@@ -44,6 +44,21 @@ vi.mock("../src/environment/index.js", async (importOriginal) => {
|
||||
return { ...mod, Environment: CapturingEnvironment };
|
||||
});
|
||||
|
||||
// Captures every GenerativeModelConfig buildRuntime constructs: the effective thinking level
|
||||
// is no longer observable through session_meta (it holds invariants only) — assertions read
|
||||
// the construction default from the captured config instead.
|
||||
const capturedLLMConfigs = vi.hoisted(() => ({ list: [] as { thinkingLevel?: string }[] }));
|
||||
vi.mock("../src/llm/index.js", async (importOriginal) => {
|
||||
const mod = await importOriginal<typeof import("../src/llm/index.js")>();
|
||||
class CapturingGenerativeModel extends mod.GenerativeModel {
|
||||
constructor(config: ConstructorParameters<typeof mod.GenerativeModel>[0]) {
|
||||
super(config);
|
||||
capturedLLMConfigs.list.push(config as { thinkingLevel?: string });
|
||||
}
|
||||
}
|
||||
return { ...mod, GenerativeModel: CapturingGenerativeModel };
|
||||
});
|
||||
|
||||
let tmpRoot: string;
|
||||
let prevHome: string | undefined;
|
||||
let restoreKeys: () => void;
|
||||
@@ -247,12 +262,13 @@ describe("Agent.createSession session source (session_meta origin marker)", () =
|
||||
});
|
||||
|
||||
describe("Agent.createSession thinking level (explicit option wins over the Agent config)", () => {
|
||||
const uniThinkingOf = (llm: unknown): unknown =>
|
||||
((llm as { uniConfig?: { thinking_level?: unknown } }).uniConfig ?? {}).thinking_level;
|
||||
const llmOf = (session: unknown): unknown =>
|
||||
(session as { engine: { deps: { llm: unknown } } }).engine.deps.llm;
|
||||
// The session's default level lives on the LLM object (per-request overrides fall back to
|
||||
// it); session_meta holds per-session invariants only and never records a thinking level.
|
||||
const defaultLevelOf = (session: unknown): unknown =>
|
||||
(session as { engine: { deps: { llm: { defaultThinkingLevel?: unknown } } } }).engine.deps.llm
|
||||
.defaultThinkingLevel;
|
||||
|
||||
it("falls back to the Agent config for both the meta echo and the llm config", async () => {
|
||||
it("falls back to the Agent config for the llm default; session_meta records no level", async () => {
|
||||
const agent = await createAgent();
|
||||
// The seeded Agent config pins thinking_level "medium" — the only source when no option is given.
|
||||
expect(agent.state.systemConfig.model?.thinking_level).toBe("medium");
|
||||
@@ -260,10 +276,12 @@ describe("Agent.createSession thinking level (explicit option wins over the Agen
|
||||
await fs.mkdir(ws, { recursive: true });
|
||||
const session = await agent.createSession({ workspaceDir: ws });
|
||||
try {
|
||||
expect((session.metaMessage.payload as { thinking_level: string }).thinking_level).toBe(
|
||||
"medium",
|
||||
);
|
||||
expect(uniThinkingOf(llmOf(session))).toBe(mapThinkingLevel("medium"));
|
||||
// session_meta contains ONLY per-session invariants: the thinking level is per-turn.
|
||||
expect(
|
||||
"thinking_level" in (session.metaMessage.payload as unknown as Record<string, unknown>),
|
||||
).toBe(false);
|
||||
expect(defaultLevelOf(session)).toBe("medium");
|
||||
expect(mapThinkingLevel("medium")).toBeDefined(); // the name maps onto the wire enum
|
||||
} finally {
|
||||
session.dispose();
|
||||
}
|
||||
@@ -275,10 +293,7 @@ describe("Agent.createSession thinking level (explicit option wins over the Agen
|
||||
await fs.mkdir(ws, { recursive: true });
|
||||
const session = await agent.createSession({ workspaceDir: ws, thinkingLevel: "high" });
|
||||
try {
|
||||
expect((session.metaMessage.payload as { thinking_level: string }).thinking_level).toBe(
|
||||
"high",
|
||||
);
|
||||
expect(uniThinkingOf(llmOf(session))).toBe(mapThinkingLevel("high"));
|
||||
expect(defaultLevelOf(session)).toBe("high");
|
||||
} finally {
|
||||
session.dispose();
|
||||
}
|
||||
@@ -297,7 +312,9 @@ describe("run_subagent spawning follows the PARENT session (never the Project de
|
||||
/**
|
||||
* Spawns through the real runner and reads the child session_meta — the first message
|
||||
* handle.run yields, emitted before any LLM request; the run generator is closed right
|
||||
* after, so nothing is ever sent upstream.
|
||||
* after, so nothing is ever sent upstream. The child's effective thinking level is no
|
||||
* longer in session_meta (invariants only): it's read from the captured LLM config the
|
||||
* spawn constructed (the last one pushed).
|
||||
*/
|
||||
async function spawnedChildMeta(
|
||||
runner: SubagentRunner,
|
||||
@@ -305,11 +322,12 @@ describe("run_subagent spawning follows the PARENT session (never the Project de
|
||||
): Promise<{
|
||||
provider: string;
|
||||
model_id: string;
|
||||
thinking_level: string;
|
||||
workspace: string;
|
||||
source?: string;
|
||||
llm: { thinkingLevel?: string };
|
||||
}> {
|
||||
const handle = await runner.spawn(input);
|
||||
const llm = capturedLLMConfigs.list.at(-1)!;
|
||||
try {
|
||||
const gen = handle.run({ prompt: "noop" });
|
||||
const first = await gen.next();
|
||||
@@ -319,12 +337,14 @@ describe("run_subagent spawning follows the PARENT session (never the Project de
|
||||
// Child messages are stamped with the child Session id as the origin hop.
|
||||
expect(msg.origin?.[0]).toBe(handle.sessionId);
|
||||
await gen.return(undefined);
|
||||
return msg.payload as {
|
||||
provider: string;
|
||||
model_id: string;
|
||||
thinking_level: string;
|
||||
workspace: string;
|
||||
source?: string;
|
||||
return {
|
||||
...(msg.payload as {
|
||||
provider: string;
|
||||
model_id: string;
|
||||
workspace: string;
|
||||
source?: string;
|
||||
}),
|
||||
llm,
|
||||
};
|
||||
} finally {
|
||||
handle.dispose();
|
||||
@@ -350,7 +370,7 @@ describe("run_subagent spawning follows the PARENT session (never the Project de
|
||||
const child = await spawnedChildMeta(runner, {});
|
||||
expect(child.provider).toBe("anthropic");
|
||||
expect(child.model_id).toBe("claude-sonnet-4-6");
|
||||
expect(child.thinking_level).toBe("high");
|
||||
expect(child.llm.thinkingLevel).toBe("high");
|
||||
// Workspace inheritance (behavior that predates model/thinking inheritance): locked here.
|
||||
expect(child.workspace).toBe(ws);
|
||||
// The spawn site marks the child's own session_meta as subagent-created — the single
|
||||
@@ -379,7 +399,7 @@ describe("run_subagent spawning follows the PARENT session (never the Project de
|
||||
expect(child.provider).toBe("deepseek");
|
||||
expect(child.model_id).toBe("deepseek-v4-pro");
|
||||
// Thinking level and workspace are inherited implicitly even with an explicit model.
|
||||
expect(child.thinking_level).toBe("medium");
|
||||
expect(child.llm.thinkingLevel).toBe("medium");
|
||||
expect(child.workspace).toBe(ws);
|
||||
} finally {
|
||||
parent.dispose();
|
||||
@@ -396,13 +416,13 @@ describe("run_subagent spawning follows the PARENT session (never the Project de
|
||||
const ws = path.join(tmpRoot, "ws-inherit-none");
|
||||
await fs.mkdir(ws, { recursive: true });
|
||||
const parent = await agent.createSession({ workspaceDir: ws });
|
||||
const parentLLM = capturedLLMConfigs.list.at(-1)!;
|
||||
const runner = lastSpawnedRunner();
|
||||
try {
|
||||
expect((parent.metaMessage.payload as { thinking_level: string }).thinking_level).toBe(
|
||||
"default",
|
||||
);
|
||||
expect("thinkingLevel" in parentLLM).toBe(false);
|
||||
const child = await spawnedChildMeta(runner, { agentId: "helper_agent" });
|
||||
expect(child.thinking_level).toBe("default");
|
||||
// The tri-state null reached the child: no level at all, not helper_agent's "medium".
|
||||
expect("thinkingLevel" in child.llm).toBe(false);
|
||||
} finally {
|
||||
parent.dispose();
|
||||
}
|
||||
@@ -447,7 +467,7 @@ describe("run_subagent spawning follows the PARENT session (never the Project de
|
||||
const child = await spawnedChildMeta(runner, { agentId: "helper_agent" });
|
||||
expect(child.provider).toBe("anthropic");
|
||||
expect(child.model_id).toBe("claude-sonnet-4-6");
|
||||
expect(child.thinking_level).toBe("xhigh");
|
||||
expect(child.llm.thinkingLevel).toBe("xhigh");
|
||||
expect(child.workspace).toBe(ws);
|
||||
} finally {
|
||||
parent.dispose();
|
||||
|
||||
@@ -115,7 +115,6 @@ const metaMessage = sessionMeta({
|
||||
model_context_window: 200000,
|
||||
system_prompt: "sp",
|
||||
tools: [],
|
||||
thinking_level: "default",
|
||||
agent_state: "/tmp/state",
|
||||
workspace: "/tmp/ws",
|
||||
});
|
||||
|
||||
@@ -580,7 +580,6 @@ describe("ContextEngine ReAct loop (mock LLM, approve callback)", () => {
|
||||
model_context_window: 1000,
|
||||
system_prompt: "sys",
|
||||
tools: [],
|
||||
thinking_level: "medium",
|
||||
agent_state: "/root/p/worker/agent_state",
|
||||
workspace: "/tmp/w",
|
||||
});
|
||||
@@ -666,6 +665,66 @@ describe("ContextEngine ReAct loop (mock LLM, approve callback)", () => {
|
||||
).toBe(false);
|
||||
expect(recorded.filter((m) => (m.payload as { text?: string }).text === "go")).toHaveLength(1);
|
||||
});
|
||||
|
||||
it("forwards RunOptions.thinkingLevel to every LLM request of the run (reconnects included); compaction keeps the default", async () => {
|
||||
const levels: (string | undefined)[] = [];
|
||||
let calls = 0;
|
||||
const llm: LLMInterface = {
|
||||
async *streamGenerate(params) {
|
||||
calls += 1;
|
||||
levels.push(params.thinkingLevel);
|
||||
if (calls === 1) {
|
||||
// First attempt drops: the reconnect retry must carry the same per-turn level.
|
||||
yield assistantText("half", "timeout");
|
||||
return { status: "timeout" };
|
||||
}
|
||||
if (calls === 2) {
|
||||
// Retry completes with usage above the compaction threshold → a Task-boundary
|
||||
// summarize compaction issues one more request (the engine's, not this run's turn):
|
||||
// it must NOT carry the per-turn override.
|
||||
yield assistantText("recovered");
|
||||
yield tokenUsage(emptyTokenCounts(), {
|
||||
cache_read: 0,
|
||||
cache_write: 0,
|
||||
output: 1,
|
||||
total: 100,
|
||||
});
|
||||
return { status: "completed" };
|
||||
}
|
||||
yield assistantText("<summary>s</summary>");
|
||||
yield tokenUsage(emptyTokenCounts(), {
|
||||
cache_read: 0,
|
||||
cache_write: 0,
|
||||
output: 1,
|
||||
total: 5,
|
||||
});
|
||||
return { status: "completed" };
|
||||
},
|
||||
};
|
||||
const environment = new Environment({
|
||||
workspaceDir: workspace,
|
||||
toolConfig: execCommandToolConfig(),
|
||||
});
|
||||
const engine = new ContextEngine({
|
||||
llm,
|
||||
environment,
|
||||
maxReconnects: 1,
|
||||
reconnectBackoffMs: 1,
|
||||
createLLM: () => llm,
|
||||
compaction: { maxContextLength: 10, maxSessionTurns: -1, mode: "summarize", prompt: "SUM" },
|
||||
});
|
||||
|
||||
const all: OmniMessage[] = [];
|
||||
for await (const msg of engine.run([userText("go")], {
|
||||
approve: allowAll,
|
||||
thinkingLevel: "high",
|
||||
})) {
|
||||
all.push(msg);
|
||||
}
|
||||
expect(calls).toBe(3);
|
||||
// Turn attempt + reconnect retry carry the run's level; the compaction request does not.
|
||||
expect(levels).toEqual(["high", "high", undefined]);
|
||||
});
|
||||
});
|
||||
|
||||
describe("ContextEngine async/incremental tool calls (overlapping execution)", () => {
|
||||
|
||||
@@ -15,8 +15,8 @@ import {
|
||||
ThinkingLevel,
|
||||
ToolCallArgumentParseError,
|
||||
} from "@prismshadow/agenthub";
|
||||
import type { UniEvent, UniMessage, UsageMetadata } from "@prismshadow/agenthub";
|
||||
import type { LLMOutcome } from "../src/interfaces.js";
|
||||
import type { UniConfig, UniEvent, UniMessage, UsageMetadata } from "@prismshadow/agenthub";
|
||||
import type { LLMOutcome, ThinkingLevelName } from "../src/interfaces.js";
|
||||
|
||||
import {
|
||||
EventTranslator,
|
||||
@@ -1034,7 +1034,7 @@ describe("config helpers", () => {
|
||||
expect("parameters" in schemas[1]!).toBe(false);
|
||||
});
|
||||
|
||||
it("builds UniConfig with only provided fields", () => {
|
||||
it("builds UniConfig with only provided fields (thinking level stays out — it is per-request)", () => {
|
||||
const cfg = buildUniConfig({
|
||||
modelId: "claude-sonnet-4-6",
|
||||
tools: [{ name: "t", description: "d" }],
|
||||
@@ -1044,7 +1044,9 @@ describe("config helpers", () => {
|
||||
});
|
||||
expect(cfg.system_prompt).toBe("You are concise.");
|
||||
expect(cfg.max_tokens).toBe(256);
|
||||
expect(cfg.thinking_level).toBe(ThinkingLevel.HIGH);
|
||||
// The thinking level is applied per request (override ?? construction default), never
|
||||
// baked into the frozen config — see the request-config test below.
|
||||
expect("thinking_level" in cfg).toBe(false);
|
||||
expect(cfg.tools).toEqual([{ name: "t", description: "d" }]);
|
||||
|
||||
const minimal = buildUniConfig({ modelId: "m", tools: [] });
|
||||
@@ -1181,6 +1183,72 @@ describe("isIncompleteStreamError", () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe("GenerativeModel per-request thinking level", () => {
|
||||
// Captures the UniConfig each request goes out with (the openStream seam now receives the
|
||||
// per-request resolved config): the effective level = params.thinkingLevel ?? the
|
||||
// construction default, mapped onto the wire enum; neither → the key stays off the wire.
|
||||
function capturingModel(defaultLevel?: ThinkingLevelName): {
|
||||
model: GenerativeModel;
|
||||
configs: (UniConfig | undefined)[];
|
||||
} {
|
||||
const configs: (UniConfig | undefined)[] = [];
|
||||
class CapturingModel extends GenerativeModel {
|
||||
protected override openStream(
|
||||
_uni: UniMessage,
|
||||
_signal: AbortSignal,
|
||||
config?: UniConfig,
|
||||
): AsyncIterable<UniEvent> {
|
||||
configs.push(config);
|
||||
return (async function* () {
|
||||
yield ev({
|
||||
content_items: [{ type: "text", text: "ok" }],
|
||||
finish_reason: "stop",
|
||||
usage_metadata: {
|
||||
cached_tokens: 0,
|
||||
prompt_tokens: 1,
|
||||
thoughts_tokens: 0,
|
||||
response_tokens: 1,
|
||||
},
|
||||
});
|
||||
})();
|
||||
}
|
||||
}
|
||||
const model = new CapturingModel({
|
||||
modelId: "claude-sonnet-4-6",
|
||||
tools: [],
|
||||
...(defaultLevel !== undefined ? { thinkingLevel: defaultLevel } : {}),
|
||||
});
|
||||
return { model, configs };
|
||||
}
|
||||
|
||||
async function drainAll(gen: AsyncGenerator<OmniMessage, LLMOutcome | void>): Promise<void> {
|
||||
let res = await gen.next();
|
||||
while (!res.done) res = await gen.next();
|
||||
}
|
||||
|
||||
it("applies the construction default when no override is given, per request", async () => {
|
||||
const { model, configs } = capturingModel("medium");
|
||||
await drainAll(model.streamGenerate({ newMessages: [userText("hi")] }));
|
||||
expect(configs[0]?.thinking_level).toBe(ThinkingLevel.MEDIUM);
|
||||
});
|
||||
|
||||
it("a per-request override wins for that request only; the default returns afterwards", async () => {
|
||||
const { model, configs } = capturingModel("medium");
|
||||
await drainAll(model.streamGenerate({ newMessages: [userText("a")], thinkingLevel: "high" }));
|
||||
await drainAll(model.streamGenerate({ newMessages: [userText("b")] }));
|
||||
expect(configs[0]?.thinking_level).toBe(ThinkingLevel.HIGH);
|
||||
expect(configs[1]?.thinking_level).toBe(ThinkingLevel.MEDIUM);
|
||||
});
|
||||
|
||||
it("no default and no override: thinking_level stays off the wire; an override still applies", async () => {
|
||||
const { model, configs } = capturingModel();
|
||||
await drainAll(model.streamGenerate({ newMessages: [userText("a")] }));
|
||||
await drainAll(model.streamGenerate({ newMessages: [userText("b")], thinkingLevel: "xhigh" }));
|
||||
expect(configs[0] !== undefined && "thinking_level" in configs[0]).toBe(false);
|
||||
expect(configs[1]?.thinking_level).toBe(ThinkingLevel.XHIGH);
|
||||
});
|
||||
});
|
||||
|
||||
describe("GenerativeModel.streamGenerate outcome classification (PRN-013)", () => {
|
||||
// Injects a controlled UniEvent stream through the protected openStream seam to verify the
|
||||
// outcome classification of timeout/network-drop/interrupt/error, without needing a real API.
|
||||
|
||||
@@ -50,7 +50,6 @@ function meta(): OmniMessage {
|
||||
model_context_window: 1000000,
|
||||
system_prompt: "SP",
|
||||
tools: [],
|
||||
thinking_level: "default",
|
||||
agent_state: "/agent/state",
|
||||
workspace: "/ws",
|
||||
});
|
||||
|
||||
@@ -24,7 +24,7 @@ import {
|
||||
userText,
|
||||
} from "../src/omnimessage/index.js";
|
||||
import type { OmniMessage, TokenCounts } from "../src/omnimessage/index.js";
|
||||
import { GenerativeModel, groupHistoryToUniMessages, mapThinkingLevel } from "../src/llm/index.js";
|
||||
import { GenerativeModel, groupHistoryToUniMessages } from "../src/llm/index.js";
|
||||
import { readTrace } from "../src/trace/index.js";
|
||||
import { tracesDir } from "../src/state/paths.js";
|
||||
import { stubProviderKeys } from "./provider-keys.js";
|
||||
@@ -90,7 +90,6 @@ function metaFor(
|
||||
model_context_window: 1000000,
|
||||
system_prompt: "ORIGINAL SYSTEM PROMPT",
|
||||
tools: [],
|
||||
thinking_level: "default",
|
||||
agent_state: "/agent/state",
|
||||
workspace: workspaceDir,
|
||||
...(source !== undefined ? { source } : {}),
|
||||
@@ -176,35 +175,40 @@ describe("agent.resumeSession", () => {
|
||||
).toEqual(["text", "text", "abort"]);
|
||||
});
|
||||
|
||||
it("restores the recorded thinking level; the literal 'default' falls back to the Agent config", async () => {
|
||||
// A resumed subagent session must keep the level it inherited from its parent (recorded
|
||||
// in session_meta, like model/Workspace/system prompt) — never silently re-read this
|
||||
// Agent's own config. The seeded Agent config here pins "medium".
|
||||
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".
|
||||
const agent = await createAgent({});
|
||||
expect(agent.state.systemConfig.model?.thinking_level).toBe("medium");
|
||||
const levelOf = (session: unknown): unknown =>
|
||||
(
|
||||
(session as { engine: { deps: { llm: { uniConfig?: { thinking_level?: unknown } } } } })
|
||||
.engine.deps.llm.uniConfig ?? {}
|
||||
).thinking_level;
|
||||
(session as { engine: { deps: { llm: { defaultThinkingLevel?: unknown } } } }).engine.deps.llm
|
||||
.defaultThinkingLevel;
|
||||
|
||||
const recorded = metaFor(SID, workspace);
|
||||
(recorded.payload as { thinking_level: string }).thinking_level = "xhigh";
|
||||
// A legacy trace: inject the retired field loosely into the on-disk meta JSON.
|
||||
(recorded.payload as unknown as Record<string, unknown>).thinking_level = "xhigh";
|
||||
await writeTraceFile(tmpRoot, SID, [recorded, userText("hello")]);
|
||||
const inherited = await agent.resumeSession({ sessionId: SID });
|
||||
expect((inherited.metaMessage.payload as { thinking_level: string }).thinking_level).toBe(
|
||||
"xhigh",
|
||||
);
|
||||
expect(levelOf(inherited)).toBe(mapThinkingLevel("xhigh"));
|
||||
expect(levelOf(inherited)).toBe("xhigh");
|
||||
// The rebuilt meta holds invariants only: the legacy field is honored but never re-recorded.
|
||||
expect(
|
||||
"thinking_level" in (inherited.metaMessage.payload as unknown as Record<string, unknown>),
|
||||
).toBe(false);
|
||||
|
||||
// "default" records "no level": resume falls back to the Agent's current config, as before.
|
||||
// A current trace (no field) — and the legacy literal "default" — fall back to the Agent config.
|
||||
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 });
|
||||
expect((fallback.metaMessage.payload as { thinking_level: string }).thinking_level).toBe(
|
||||
"medium",
|
||||
);
|
||||
expect(levelOf(fallback)).toBe(mapThinkingLevel("medium"));
|
||||
expect(levelOf(fallback)).toBe("medium");
|
||||
|
||||
const SID3 = "session-2026-07-06-12-00-00-abcdef03";
|
||||
const legacyDefault = metaFor(SID3, workspace);
|
||||
(legacyDefault.payload as unknown as Record<string, unknown>).thinking_level = "default";
|
||||
await writeTraceFile(tmpRoot, SID3, [legacyDefault, userText("hi")]);
|
||||
const viaDefault = await agent.resumeSession({ sessionId: SID3 });
|
||||
expect(levelOf(viaDefault)).toBe("medium");
|
||||
});
|
||||
|
||||
it("does not write pairing placeholders to the trace file (resume is side-effect free)", async () => {
|
||||
|
||||
@@ -55,7 +55,6 @@ const META: SessionMetaPayload = {
|
||||
model_context_window: 1000,
|
||||
system_prompt: "sp",
|
||||
tools: [],
|
||||
thinking_level: "default",
|
||||
agent_state: "/tmp/state",
|
||||
workspace: "/tmp/w",
|
||||
};
|
||||
@@ -137,6 +136,12 @@ describe("session-title", () => {
|
||||
expect(stripConversationMarkers("<handoff_from>data_analyst</handoff_from>继续分析")).toBe(
|
||||
"继续分析",
|
||||
);
|
||||
// The /model switch origin block (the new session's first message) must not leak into the title either.
|
||||
expect(
|
||||
stripConversationMarkers(
|
||||
"<model_switch_from>\nsession: session-01\ntrace: /t/x_001.jsonl\n</model_switch_from>\n继续这个任务",
|
||||
),
|
||||
).toBe("继续这个任务");
|
||||
expect(stripConversationMarkers("render a <div> element")).toBe("render a <div> element");
|
||||
});
|
||||
|
||||
|
||||
@@ -25,7 +25,6 @@ function meta() {
|
||||
model_context_window: 200000,
|
||||
system_prompt: "test system prompt",
|
||||
tools: [{ name: "exec_command", description: "test tool" }],
|
||||
thinking_level: "medium",
|
||||
agent_state: "/tmp/agent_state",
|
||||
workspace: "/tmp/workspace",
|
||||
});
|
||||
@@ -87,7 +86,6 @@ describe("Writer", () => {
|
||||
model_context_window: 200000,
|
||||
system_prompt: "child prompt",
|
||||
tools: [],
|
||||
thinking_level: "medium",
|
||||
agent_state: "/tmp/child_agent/agent_state",
|
||||
workspace: "/tmp/workspace",
|
||||
});
|
||||
|
||||
Reference in new issue
Block a user