






















@@ -39,6 +39,17 @@ function overview(overrides: Partial<CrestodianOverview["tools"]> = {}): Crestod
3939};
4040}
414142+function requireRecord(value: unknown): Record<string, unknown> {
43+expect(value).toBeTruthy();
44+expect(typeof value).toBe("object");
45+expect(Array.isArray(value)).toBe(false);
46+return value as Record<string, unknown>;
47+}
48+49+function firstMockArg(mock: ReturnType<typeof vi.fn>): Record<string, unknown> {
50+return requireRecord(mock.mock.calls[0]?.[0]);
51+}
52+4253describe("Crestodian assistant", () => {
4354it("parses the first compact JSON command", () => {
4455expect(
@@ -108,34 +119,37 @@ describe("Crestodian assistant", () => {
108119);
109120const runEmbeddedPiAgent = vi.fn();
110121111-await expect(
112-planCrestodianCommandWithLocalRuntime({
113-input: "what is going on",
114-overview: overview({
115-claude: { command: "claude", found: true },
116-codex: { command: "codex", found: true },
117-}),
118-deps: {
119- runCliAgent,
120- runEmbeddedPiAgent,
121-createTempDir: async () => "/tmp/crestodian-planner",
122-removeTempDir: async () => {},
123-},
122+const result = await planCrestodianCommandWithLocalRuntime({
123+input: "what is going on",
124+overview: overview({
125+claude: { command: "claude", found: true },
126+codex: { command: "codex", found: true },
124127}),
125-).resolves.toMatchObject({
126-command: "status",
127-reply: "Checking the shell.",
128-modelLabel: "claude-cli/claude-opus-4-7",
128+deps: {
129+ runCliAgent,
130+ runEmbeddedPiAgent,
131+createTempDir: async () => "/tmp/crestodian-planner",
132+removeTempDir: async () => {},
133+},
129134});
135+expect(result).not.toBeNull();
136+if (result === null) {
137+throw new Error("Expected planner result");
138+}
139+expect(result.command).toBe("status");
140+expect(result.reply).toBe("Checking the shell.");
141+expect(result.modelLabel).toBe("claude-cli/claude-opus-4-7");
130142131143expect(runCliAgent).toHaveBeenCalledTimes(1);
132-const firstCliCall = runCliAgent.mock.calls[0][0];
133-expect(firstCliCall).toMatchObject({
134-provider: "claude-cli",
135-model: "claude-opus-4-7",
136-cleanupCliLiveSessionOnRunEnd: true,
137-});
138-expect(firstCliCall.config?.agents?.defaults?.cliBackends).toBeUndefined();
144+const firstCliCall = firstMockArg(runCliAgent);
145+expect(firstCliCall.provider).toBe("claude-cli");
146+expect(firstCliCall.model).toBe("claude-opus-4-7");
147+expect(firstCliCall.cleanupCliLiveSessionOnRunEnd).toBe(true);
148+const firstCliConfig = requireRecord(firstCliCall.config);
149+const firstCliAgents = requireRecord(firstCliConfig.agents);
150+const firstCliDefaults = requireRecord(firstCliAgents.defaults);
151+expect(firstCliDefaults.cliBackends).toBeUndefined();
152+expect(firstCliCall.extraSystemPrompt).toBeTypeOf("string");
139153expect(firstCliCall.extraSystemPrompt).toContain("Do not use tools, shell commands");
140154expect(runEmbeddedPiAgent).not.toHaveBeenCalled();
141155});
@@ -155,23 +169,23 @@ describe("Crestodian assistant", () => {
155169codex: { command: "codex", found: true },
156170}),
157171);
158-expect(codexAppServer?.buildConfig("/tmp/workspace")).toMatchObject({
159- agents: {
160- defaults: {
161- workspace: "/tmp/workspace",
162- model: { primary: "openai/gpt-5.5" },
163- },
164- },
165- plugins: { entries: { codex: { enabled: true } } },
166-});
167-expect(codexCli?.buildConfig("/tmp/workspace")).toMatchObject({
168- agents: {
169- defaults: {
170- workspace: "/tmp/workspace",
171- model: { primary: "codex-cli/gpt-5.5" },
172- },
173- },
174-});
172+const codexAppServerConfig = requireRecord(codexAppServer?.buildConfig("/tmp/workspace"));
173+const codexAppServerAgents = requireRecord(codexAppServerConfig.agents);
174+const codexAppServerDefaults = requireRecord(codexAppServerAgents.defaults);
175+const codexAppServerModel = requireRecord(codexAppServerDefaults.model);
176+const codexAppServerPlugins = requireRecord(codexAppServerConfig.plugins);
177+const codexAppServerEntries = requireRecord(codexAppServerPlugins.entries);
178+const codexAppServerCodexEntry = requireRecord(codexAppServerEntries.codex);
179+expect(codexAppServerDefaults.workspace).toBe("/tmp/workspace");
180+expect(codexAppServerModel.primary).toBe("openai/gpt-5.5");
181+expect(codexAppServerCodexEntry.enabled).toBe(true);
182+183+const codexCliConfig = requireRecord(codexCli?.buildConfig("/tmp/workspace"));
184+const codexCliAgents = requireRecord(codexCliConfig.agents);
185+const codexCliDefaults = requireRecord(codexCliAgents.defaults);
186+const codexCliModel = requireRecord(codexCliDefaults.model);
187+expect(codexCliDefaults.workspace).toBe("/tmp/workspace");
188+expect(codexCliModel.primary).toBe("codex-cli/gpt-5.5");
175189});
176190177191it("falls back to Codex app-server when Claude CLI planning fails", async () => {
@@ -187,43 +201,43 @@ describe("Crestodian assistant", () => {
187201}),
188202);
189203190-await expect(
191-planCrestodianCommandWithLocalRuntime({
192-input: "is gateway alive",
193-overview: overview({
194-claude: { command: "claude", found: true },
195-codex: { command: "codex", found: true },
196-}),
197-deps: {
198- runCliAgent,
199- runEmbeddedPiAgent,
200-createTempDir: async () => "/tmp/crestodian-planner",
201-removeTempDir: async () => {},
202-},
204+const result = await planCrestodianCommandWithLocalRuntime({
205+input: "is gateway alive",
206+overview: overview({
207+claude: { command: "claude", found: true },
208+codex: { command: "codex", found: true },
203209}),
204-).resolves.toMatchObject({
205-command: "gateway status",
206-reply: "Codex planner online.",
207-modelLabel: "openai/gpt-5.5 via codex",
210+deps: {
211+ runCliAgent,
212+ runEmbeddedPiAgent,
213+createTempDir: async () => "/tmp/crestodian-planner",
214+removeTempDir: async () => {},
215+},
208216});
217+expect(result).not.toBeNull();
218+if (result === null) {
219+throw new Error("Expected planner result");
220+}
221+expect(result.command).toBe("gateway status");
222+expect(result.reply).toBe("Codex planner online.");
223+expect(result.modelLabel).toBe("openai/gpt-5.5 via codex");
209224210225expect(runEmbeddedPiAgent).toHaveBeenCalledTimes(1);
211-const firstEmbeddedCall = runEmbeddedPiAgent.mock.calls[0][0];
212-expect(firstEmbeddedCall).toMatchObject({
213-provider: "openai",
214-model: "gpt-5.5",
215-agentHarnessId: "codex",
216-disableTools: true,
217-toolsAllow: [],
218-});
219-expect(firstEmbeddedCall.config).toMatchObject({
220-agents: {
221-defaults: {
222-model: { primary: "openai/gpt-5.5" },
223-},
224-},
225-plugins: { entries: { codex: { enabled: true } } },
226-});
226+const firstEmbeddedCall = firstMockArg(runEmbeddedPiAgent);
227+expect(firstEmbeddedCall.provider).toBe("openai");
228+expect(firstEmbeddedCall.model).toBe("gpt-5.5");
229+expect(firstEmbeddedCall.agentHarnessId).toBe("codex");
230+expect(firstEmbeddedCall.disableTools).toBe(true);
231+expect(firstEmbeddedCall.toolsAllow).toEqual([]);
232+const embeddedConfig = requireRecord(firstEmbeddedCall.config);
233+const embeddedAgents = requireRecord(embeddedConfig.agents);
234+const embeddedDefaults = requireRecord(embeddedAgents.defaults);
235+const embeddedModel = requireRecord(embeddedDefaults.model);
236+const embeddedPlugins = requireRecord(embeddedConfig.plugins);
237+const embeddedEntries = requireRecord(embeddedPlugins.entries);
238+const embeddedCodexEntry = requireRecord(embeddedEntries.codex);
239+expect(embeddedModel.primary).toBe("openai/gpt-5.5");
240+expect(embeddedCodexEntry.enabled).toBe(true);
227241});
228242229243it("uses Codex CLI if the app-server planner is not usable", async () => {
@@ -240,31 +254,31 @@ describe("Crestodian assistant", () => {
240254throw new Error("codex app-server unavailable");
241255});
242256243-await expect(
244-planCrestodianCommandWithLocalRuntime({
245-input: "show models",
246-overview: overview({
247-codex: { command: "codex", found: true },
248-}),
249-deps: {
250- runCliAgent,
251- runEmbeddedPiAgent,
252-createTempDir: async () => "/tmp/crestodian-planner",
253-removeTempDir: async () => {},
254-},
257+const result = await planCrestodianCommandWithLocalRuntime({
258+input: "show models",
259+overview: overview({
260+codex: { command: "codex", found: true },
255261}),
256-).resolves.toMatchObject({
257-command: "models",
258-reply: "CLI fallback.",
259-modelLabel: "codex-cli/gpt-5.5",
262+deps: {
263+ runCliAgent,
264+ runEmbeddedPiAgent,
265+createTempDir: async () => "/tmp/crestodian-planner",
266+removeTempDir: async () => {},
267+},
260268});
269+expect(result).not.toBeNull();
270+if (result === null) {
271+throw new Error("Expected planner result");
272+}
273+expect(result.command).toBe("models");
274+expect(result.reply).toBe("CLI fallback.");
275+expect(result.modelLabel).toBe("codex-cli/gpt-5.5");
261276262277expect(runEmbeddedPiAgent).toHaveBeenCalledTimes(1);
263278expect(runCliAgent).toHaveBeenCalledTimes(1);
264-expect(runCliAgent.mock.calls[0][0]).toMatchObject({
265-provider: "codex-cli",
266-model: "gpt-5.5",
267-cleanupCliLiveSessionOnRunEnd: true,
268-});
279+const firstCliCall = firstMockArg(runCliAgent);
280+expect(firstCliCall.provider).toBe("codex-cli");
281+expect(firstCliCall.model).toBe("gpt-5.5");
282+expect(firstCliCall.cleanupCliLiveSessionOnRunEnd).toBe(true);
269283});
270284});
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。