mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-06 06:50:43 +00:00
fix: support Lobster approvalId TaskFlow resumes (#69559)
This commit is contained in:
@@ -19,6 +19,7 @@ Docs: https://docs.openclaw.ai
|
||||
### Fixes
|
||||
|
||||
- OpenAI/Responses: resolve `/think` levels against each GPT model's supported reasoning efforts so `/think off` no longer becomes high reasoning or sends unsupported `reasoning.effort: "none"` payloads.
|
||||
- Lobster/TaskFlow: allow managed approval resumes to use `approvalId` without a resume token, and persist that id in approval wait state. (#69559) Thanks @kirkluokun.
|
||||
- Setup/TUI: relaunch the setup hatch TUI in a fresh process while preserving the configured gateway target and auth source, so onboarding recovers terminal state cleanly without exposing gateway secrets on command-line args. (#69524) Thanks @shakkernerd.
|
||||
- Codex: avoid re-exposing the image-generation tool on native vision turns with inbound images, and keep bare image-model overrides on the configured image provider. (#65061) Thanks @zhulijin1991.
|
||||
- Sessions/reset: clear auto-sourced model, provider, and auth-profile overrides on `/new` and `/reset` while preserving explicit user selections, so channel sessions stop staying pinned to runtime fallback choices. (#69419) Thanks @sk7n4k3d.
|
||||
|
||||
@@ -23,6 +23,7 @@ export type LobsterApprovalWaitState = {
|
||||
prompt: string;
|
||||
items: JsonLike[];
|
||||
resumeToken?: string;
|
||||
approvalId?: string;
|
||||
};
|
||||
|
||||
export type RunManagedLobsterFlowParams = {
|
||||
@@ -41,9 +42,8 @@ export type ResumeManagedLobsterFlowParams = {
|
||||
runner: LobsterRunner;
|
||||
runnerParams: LobsterRunnerParams & {
|
||||
action: "resume";
|
||||
token: string;
|
||||
approve: boolean;
|
||||
};
|
||||
} & ({ token: string } | { approvalId: string });
|
||||
flowId: string;
|
||||
expectedRevision: number;
|
||||
currentStep?: string;
|
||||
@@ -120,6 +120,9 @@ function buildApprovalWaitState(envelope: Extract<LobsterEnvelope, { ok: true }>
|
||||
...(envelope.requiresApproval.resumeToken
|
||||
? { resumeToken: envelope.requiresApproval.resumeToken }
|
||||
: {}),
|
||||
...(envelope.requiresApproval.approvalId
|
||||
? { approvalId: envelope.requiresApproval.approvalId }
|
||||
: {}),
|
||||
} satisfies LobsterApprovalWaitState;
|
||||
}
|
||||
|
||||
|
||||
@@ -137,6 +137,7 @@ describe("lobster plugin tool", () => {
|
||||
prompt: "Approve this?",
|
||||
items: [{ id: "item-1" }],
|
||||
resumeToken: "resume-1",
|
||||
approvalId: "approval-1",
|
||||
},
|
||||
}),
|
||||
};
|
||||
@@ -168,6 +169,7 @@ describe("lobster plugin tool", () => {
|
||||
prompt: "Approve this?",
|
||||
items: [{ id: "item-1" }],
|
||||
resumeToken: "resume-1",
|
||||
approvalId: "approval-1",
|
||||
},
|
||||
});
|
||||
expect(res.details).toMatchObject({
|
||||
@@ -214,7 +216,51 @@ describe("lobster plugin tool", () => {
|
||||
).rejects.toThrow(/flowStateJson must be valid JSON/);
|
||||
});
|
||||
|
||||
it("rejects managed TaskFlow resume mode without a token", async () => {
|
||||
it("can resume managed TaskFlow mode with only approvalId", async () => {
|
||||
const runner = {
|
||||
run: vi.fn().mockResolvedValue({
|
||||
ok: true,
|
||||
status: "ok",
|
||||
output: [],
|
||||
requiresApproval: null,
|
||||
}),
|
||||
};
|
||||
const taskFlow = createFakeTaskFlow();
|
||||
const tool = createLobsterTool(fakeApi(), { runner, taskFlow });
|
||||
|
||||
const res = await tool.execute("call-managed-resume-approval-id", {
|
||||
action: "resume",
|
||||
approvalId: "approval-1",
|
||||
approve: true,
|
||||
flowId: "flow-1",
|
||||
flowExpectedRevision: 1,
|
||||
flowCurrentStep: "resume_lobster",
|
||||
});
|
||||
|
||||
expect(taskFlow.resume).toHaveBeenCalledWith({
|
||||
flowId: "flow-1",
|
||||
expectedRevision: 1,
|
||||
status: "running",
|
||||
currentStep: "resume_lobster",
|
||||
});
|
||||
expect(runner.run).toHaveBeenCalledWith({
|
||||
action: "resume",
|
||||
approvalId: "approval-1",
|
||||
approve: true,
|
||||
cwd: process.cwd(),
|
||||
timeoutMs: 20_000,
|
||||
maxStdoutBytes: 512_000,
|
||||
});
|
||||
expect(res.details).toMatchObject({
|
||||
ok: true,
|
||||
status: "ok",
|
||||
mutation: {
|
||||
applied: true,
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
it("rejects managed TaskFlow resume mode without a token or approvalId", async () => {
|
||||
const tool = createLobsterTool(fakeApi(), {
|
||||
runner: { run: vi.fn() },
|
||||
taskFlow: createFakeTaskFlow(),
|
||||
@@ -227,7 +273,7 @@ describe("lobster plugin tool", () => {
|
||||
flowExpectedRevision: 1,
|
||||
approve: true,
|
||||
}),
|
||||
).rejects.toThrow(/token required when using managed TaskFlow resume mode/);
|
||||
).rejects.toThrow(/token or approvalId required when using managed TaskFlow resume mode/);
|
||||
});
|
||||
|
||||
it("rejects managed TaskFlow resume mode without approve", async () => {
|
||||
|
||||
@@ -141,6 +141,7 @@ function parseResumeFlowParams(params: Record<string, unknown>): ManagedFlowResu
|
||||
const currentStep = readOptionalTrimmedString(params.flowCurrentStep, "flowCurrentStep");
|
||||
const waitingStep = readOptionalTrimmedString(params.flowWaitingStep, "flowWaitingStep");
|
||||
const token = readOptionalTrimmedString(params.token, "token");
|
||||
const approvalId = readOptionalTrimmedString(params.approvalId, "approvalId");
|
||||
const approve = readOptionalBoolean(params.approve, "approve");
|
||||
const runControllerId = readOptionalTrimmedString(params.flowControllerId, "flowControllerId");
|
||||
const runGoal = readOptionalTrimmedString(params.flowGoal, "flowGoal");
|
||||
@@ -164,8 +165,8 @@ function parseResumeFlowParams(params: Record<string, unknown>): ManagedFlowResu
|
||||
if (expectedRevision === undefined) {
|
||||
throw new Error("flowExpectedRevision required when using managed TaskFlow resume mode");
|
||||
}
|
||||
if (!token) {
|
||||
throw new Error("token required when using managed TaskFlow resume mode");
|
||||
if (!token && !approvalId) {
|
||||
throw new Error("token or approvalId required when using managed TaskFlow resume mode");
|
||||
}
|
||||
if (approve === undefined) {
|
||||
throw new Error("approve required when using managed TaskFlow resume mode");
|
||||
@@ -295,9 +296,8 @@ export function createLobsterTool(api: OpenClawPluginApi, options?: LobsterToolO
|
||||
runner,
|
||||
runnerParams: runnerParams as LobsterRunnerParams & {
|
||||
action: "resume";
|
||||
token: string;
|
||||
approve: boolean;
|
||||
},
|
||||
} & ({ token: string } | { approvalId: string }),
|
||||
flowId: flowParams.flowId,
|
||||
expectedRevision: flowParams.expectedRevision,
|
||||
...(flowParams.currentStep ? { currentStep: flowParams.currentStep } : {}),
|
||||
|
||||
Reference in New Issue
Block a user