


























1+import fs from "node:fs/promises";
2+import os from "node:os";
3+import path from "node:path";
4+import { afterEach, describe, expect, it } from "vitest";
5+import { getSessionEntry, upsertSessionEntry } from "../../config/sessions.js";
6+import type { OpenClawConfig } from "../../config/types.openclaw.js";
7+import { handleGoalCommand, parseGoalCommand } from "./commands-goal.js";
8+import type { HandleCommandsParams } from "./commands-types.js";
9+10+const sessionKey = "agent:main:web:main";
11+let tempRoots: string[] = [];
12+13+afterEach(async () => {
14+await Promise.all(tempRoots.map((root) => fs.rm(root, { recursive: true, force: true })));
15+tempRoots = [];
16+});
17+18+async function createStorePath(): Promise<string> {
19+const root = await fs.mkdtemp(path.join(os.tmpdir(), "openclaw-goal-command-"));
20+tempRoots.push(root);
21+return path.join(root, "sessions.json");
22+}
23+24+function buildGoalParams(commandBodyNormalized: string, storePath: string): HandleCommandsParams {
25+return {
26+cfg: {} as OpenClawConfig,
27+ctx: {
28+Provider: "web",
29+Surface: "web",
30+CommandSource: "text",
31+},
32+command: {
33+ commandBodyNormalized,
34+isAuthorizedSender: true,
35+senderIsOwner: true,
36+senderId: "tester",
37+channel: "web",
38+channelId: "web",
39+surface: "web",
40+ownerList: [],
41+rawBodyNormalized: commandBodyNormalized,
42+},
43+directives: {},
44+elevated: { enabled: true, allowed: true, failures: [] },
45+ sessionKey,
46+ storePath,
47+workspaceDir: "/tmp",
48+provider: "openai",
49+model: "gpt-5.5",
50+contextTokens: 0,
51+defaultGroupActivation: () => "mention",
52+resolvedVerboseLevel: "off",
53+resolvedReasoningLevel: "off",
54+resolveDefaultThinkingLevel: async () => undefined,
55+isGroup: false,
56+} as unknown as HandleCommandsParams;
57+}
58+59+describe("goal commands", () => {
60+it("parses bare goal text as a start objective", () => {
61+expect(parseGoalCommand("/goal build a 3d game")).toEqual({
62+action: "start",
63+text: "build a 3d game",
64+});
65+expect(parseGoalCommand("/goal --tokens 98.5K improve benchmarks")).toEqual({
66+action: "start",
67+text: "--tokens 98.5K improve benchmarks",
68+});
69+});
70+71+it("keeps explicit goal actions as controls", () => {
72+expect(parseGoalCommand("/goal status")).toEqual({ action: "status", text: "" });
73+expect(parseGoalCommand("/goal pause waiting on CI")).toEqual({
74+action: "pause",
75+text: "waiting on CI",
76+});
77+});
78+79+it("starts a goal from Codex-style bare /goal objective text", async () => {
80+const storePath = await createStorePath();
81+await upsertSessionEntry({
82+ storePath,
83+ sessionKey,
84+entry: { sessionId: "sess-main", updatedAt: 1, totalTokens: 0, totalTokensFresh: true },
85+});
86+87+const result = await handleGoalCommand(
88+buildGoalParams("/goal build a 3d game", storePath),
89+true,
90+);
91+92+expect(result?.shouldContinue).toBe(false);
93+expect(result?.reply?.text).toBe("Goal started: build a 3d game");
94+expect(getSessionEntry({ storePath, sessionKey })?.goal?.objective).toBe("build a 3d game");
95+});
96+});
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。