mirror of
https://github.com/RooVetGit/Roo-Code.git
synced 2026-09-06 08:18:39 +00:00
fix(cli): scope sessions and resume flags to current workspace (#11774)
fix(cli): scope sessions to current workspace
This commit is contained in:
parent
2f4ce367c4
commit
81047a65b6
7 changed files with 211 additions and 101 deletions
|
|
@ -85,6 +85,12 @@ export class OutputManager {
|
|||
*/
|
||||
private currentlyStreamingTs: number | null = null
|
||||
|
||||
/**
|
||||
* Track whether a say:completion_result has been streamed,
|
||||
* so the subsequent ask:completion_result doesn't duplicate the text.
|
||||
*/
|
||||
private completionResultStreamed = false
|
||||
|
||||
/**
|
||||
* Track first partial logs (for debugging first/last pattern).
|
||||
*/
|
||||
|
|
@ -197,6 +203,7 @@ export class OutputManager {
|
|||
this.displayedMessages.clear()
|
||||
this.streamedContent.clear()
|
||||
this.currentlyStreamingTs = null
|
||||
this.completionResultStreamed = false
|
||||
this.loggedFirstPartial.clear()
|
||||
this.streamingState.next({ ts: null, isStreaming: false })
|
||||
}
|
||||
|
|
@ -248,8 +255,13 @@ export class OutputManager {
|
|||
this.outputCommandOutput(ts, text, isPartial, alreadyDisplayedComplete)
|
||||
break
|
||||
|
||||
// Note: completion_result is an "ask" type, not a "say" type.
|
||||
// It is handled via the TaskCompleted event in extension-host.ts
|
||||
case "completion_result":
|
||||
// completion_result can arrive as both a "say" (with streamed text)
|
||||
// and an "ask" (handled via TaskCompleted in extension-host.ts).
|
||||
// Stream the say variant here; the ask variant is handled by
|
||||
// outputCompletionResult which will skip if already displayed.
|
||||
this.outputCompletionSayMessage(ts, text, isPartial, alreadyDisplayedComplete)
|
||||
break
|
||||
|
||||
case "error":
|
||||
if (!alreadyDisplayedComplete) {
|
||||
|
|
@ -401,13 +413,50 @@ export class OutputManager {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Output a say:completion_result message (streamed text of the completion).
|
||||
* The subsequent ask:completion_result is handled by outputCompletionResult.
|
||||
*/
|
||||
private outputCompletionSayMessage(
|
||||
ts: number,
|
||||
text: string,
|
||||
isPartial: boolean,
|
||||
alreadyDisplayedComplete: boolean | undefined,
|
||||
): void {
|
||||
if (isPartial && text) {
|
||||
this.streamContent(ts, text, "[assistant]")
|
||||
this.displayedMessages.set(ts, { ts, text, partial: true })
|
||||
this.completionResultStreamed = true
|
||||
} else if (!isPartial && text && !alreadyDisplayedComplete) {
|
||||
const streamed = this.streamedContent.get(ts)
|
||||
|
||||
if (streamed) {
|
||||
if (text.length > streamed.text.length && text.startsWith(streamed.text)) {
|
||||
const delta = text.slice(streamed.text.length)
|
||||
this.writeRaw(delta)
|
||||
}
|
||||
this.finishStream(ts)
|
||||
} else {
|
||||
this.output("\n[assistant]", text)
|
||||
}
|
||||
|
||||
this.displayedMessages.set(ts, { ts, text, partial: false })
|
||||
this.completionResultStreamed = true
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Output completion message (called from TaskCompleted handler).
|
||||
*/
|
||||
outputCompletionResult(ts: number, text: string): void {
|
||||
const previousDisplay = this.displayedMessages.get(ts)
|
||||
if (!previousDisplay || previousDisplay.partial) {
|
||||
this.output("\n[task complete]", text || "")
|
||||
if (this.completionResultStreamed) {
|
||||
// Text was already streamed via say:completion_result.
|
||||
this.output("\n[task complete]")
|
||||
} else {
|
||||
this.output("\n[task complete]", text || "")
|
||||
}
|
||||
this.displayedMessages.set(ts, { ts, text: text || "", partial: false })
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,15 +1,12 @@
|
|||
import * as os from "os"
|
||||
import * as path from "path"
|
||||
|
||||
import { readTaskSessionsFromStoragePath } from "@roo-code/core/cli"
|
||||
import { readWorkspaceTaskSessions } from "@/lib/task-history/index.js"
|
||||
|
||||
import { listSessions, parseFormat } from "../list.js"
|
||||
|
||||
vi.mock("@roo-code/core/cli", async (importOriginal) => {
|
||||
const actual = await importOriginal<typeof import("@roo-code/core/cli")>()
|
||||
vi.mock("@/lib/task-history/index.js", async (importOriginal) => {
|
||||
const actual = await importOriginal<typeof import("@/lib/task-history/index.js")>()
|
||||
return {
|
||||
...actual,
|
||||
readTaskSessionsFromStoragePath: vi.fn(),
|
||||
readWorkspaceTaskSessions: vi.fn(),
|
||||
}
|
||||
})
|
||||
|
||||
|
|
@ -42,7 +39,7 @@ describe("parseFormat", () => {
|
|||
})
|
||||
|
||||
describe("listSessions", () => {
|
||||
const storagePath = path.join(os.homedir(), ".vscode-mock", "global-storage")
|
||||
const workspacePath = process.cwd()
|
||||
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks()
|
||||
|
|
@ -60,25 +57,26 @@ describe("listSessions", () => {
|
|||
}
|
||||
|
||||
it("uses the CLI runtime storage path and prints JSON output", async () => {
|
||||
vi.mocked(readTaskSessionsFromStoragePath).mockResolvedValue([
|
||||
vi.mocked(readWorkspaceTaskSessions).mockResolvedValue([
|
||||
{ id: "s1", task: "Task 1", ts: 1_700_000_000_000, mode: "code" },
|
||||
])
|
||||
|
||||
const output = await captureStdout(() => listSessions({ format: "json" }))
|
||||
const output = await captureStdout(() => listSessions({ format: "json", workspace: workspacePath }))
|
||||
|
||||
expect(readTaskSessionsFromStoragePath).toHaveBeenCalledWith(storagePath)
|
||||
expect(readWorkspaceTaskSessions).toHaveBeenCalledWith(workspacePath)
|
||||
expect(JSON.parse(output)).toEqual({
|
||||
workspace: workspacePath,
|
||||
sessions: [{ id: "s1", task: "Task 1", ts: 1_700_000_000_000, mode: "code" }],
|
||||
})
|
||||
})
|
||||
|
||||
it("prints tab-delimited text output with ISO timestamps and formatted titles", async () => {
|
||||
vi.mocked(readTaskSessionsFromStoragePath).mockResolvedValue([
|
||||
vi.mocked(readWorkspaceTaskSessions).mockResolvedValue([
|
||||
{ id: "s1", task: "Task 1", ts: Date.UTC(2024, 0, 1, 0, 0, 0) },
|
||||
{ id: "s2", task: " ", ts: Date.UTC(2024, 0, 1, 1, 0, 0) },
|
||||
])
|
||||
|
||||
const output = await captureStdout(() => listSessions({ format: "text" }))
|
||||
const output = await captureStdout(() => listSessions({ format: "text", workspace: workspacePath }))
|
||||
const lines = output.trim().split("\n")
|
||||
|
||||
expect(lines).toEqual(["s1\t2024-01-01T00:00:00.000Z\tTask 1", "s2\t2024-01-01T01:00:00.000Z\t(untitled)"])
|
||||
|
|
|
|||
|
|
@ -1,15 +1,15 @@
|
|||
import fs from "fs"
|
||||
import os from "os"
|
||||
import path from "path"
|
||||
import { fileURLToPath } from "url"
|
||||
|
||||
import pWaitFor from "p-wait-for"
|
||||
|
||||
import { readTaskSessionsFromStoragePath, type TaskSessionEntry } from "@roo-code/core/cli"
|
||||
import type { TaskSessionEntry } from "@roo-code/core/cli"
|
||||
import type { Command, ModelRecord, WebviewMessage } from "@roo-code/types"
|
||||
import { getProviderDefaultModelId } from "@roo-code/types"
|
||||
|
||||
import { ExtensionHost, type ExtensionHostOptions } from "@/agent/index.js"
|
||||
import { readWorkspaceTaskSessions } from "@/lib/task-history/index.js"
|
||||
import { loadToken } from "@/lib/storage/index.js"
|
||||
import { getDefaultExtensionPath } from "@/lib/utils/extension.js"
|
||||
import { getApiKeyFromEnv } from "@/lib/utils/provider.js"
|
||||
|
|
@ -33,7 +33,6 @@ type CommandLike = Pick<Command, "name" | "source" | "filePath" | "description"
|
|||
type ModeLike = { slug: string; name: string }
|
||||
type SessionLike = TaskSessionEntry
|
||||
type ListHostOptions = { ephemeral: boolean }
|
||||
const DEFAULT_CLI_TASK_STORAGE_PATH = path.join(os.homedir(), ".vscode-mock", "global-storage")
|
||||
|
||||
export function parseFormat(rawFormat: string | undefined): ListFormat {
|
||||
const format = (rawFormat ?? "json").toLowerCase()
|
||||
|
|
@ -313,10 +312,11 @@ export async function listModels(options: BaseListOptions): Promise<void> {
|
|||
|
||||
export async function listSessions(options: BaseListOptions): Promise<void> {
|
||||
const format = parseFormat(options.format)
|
||||
const sessions = await readTaskSessionsFromStoragePath(DEFAULT_CLI_TASK_STORAGE_PATH)
|
||||
const workspacePath = resolveWorkspacePath(options.workspace)
|
||||
const sessions = await readWorkspaceTaskSessions(workspacePath)
|
||||
|
||||
if (format === "json") {
|
||||
outputJson({ sessions })
|
||||
outputJson({ workspace: workspacePath, sessions })
|
||||
return
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -5,7 +5,6 @@ import { fileURLToPath } from "url"
|
|||
import { createElement } from "react"
|
||||
import pWaitFor from "p-wait-for"
|
||||
|
||||
import type { HistoryItem } from "@roo-code/types"
|
||||
import { setLogger } from "@roo-code/vscode-shim"
|
||||
|
||||
import {
|
||||
|
|
@ -23,8 +22,8 @@ import { JsonEventEmitter } from "@/agent/json-event-emitter.js"
|
|||
|
||||
import { createClient } from "@/lib/sdk/index.js"
|
||||
import { loadToken, loadSettings } from "@/lib/storage/index.js"
|
||||
import { readWorkspaceTaskSessions, resolveWorkspaceResumeSessionId } from "@/lib/task-history/index.js"
|
||||
import { isRecord } from "@/lib/utils/guards.js"
|
||||
import { arePathsEqual } from "@/lib/utils/path.js"
|
||||
import { getEnvVarName, getApiKeyFromEnv } from "@/lib/utils/provider.js"
|
||||
import { runOnboarding } from "@/lib/utils/onboarding.js"
|
||||
import { getDefaultExtensionPath } from "@/lib/utils/extension.js"
|
||||
|
|
@ -105,38 +104,6 @@ async function warmRooModels(host: ExtensionHost): Promise<void> {
|
|||
})
|
||||
}
|
||||
|
||||
function extractTaskHistoryFromMessage(message: unknown): HistoryItem[] | undefined {
|
||||
if (!isRecord(message)) {
|
||||
return undefined
|
||||
}
|
||||
|
||||
if (message.type === "state") {
|
||||
const state = isRecord(message.state) ? message.state : undefined
|
||||
if (Array.isArray(state?.taskHistory)) {
|
||||
return state.taskHistory as HistoryItem[]
|
||||
}
|
||||
}
|
||||
|
||||
if (message.type === "taskHistoryUpdated" && Array.isArray(message.taskHistory)) {
|
||||
return message.taskHistory as HistoryItem[]
|
||||
}
|
||||
|
||||
return undefined
|
||||
}
|
||||
|
||||
function getMostRecentTaskIdInWorkspace(taskHistory: HistoryItem[], workspacePath: string): string | undefined {
|
||||
const workspaceTasks = taskHistory.filter(
|
||||
(item) => typeof item.workspace === "string" && arePathsEqual(item.workspace, workspacePath),
|
||||
)
|
||||
|
||||
if (workspaceTasks.length === 0) {
|
||||
return undefined
|
||||
}
|
||||
|
||||
const sorted = [...workspaceTasks].sort((a, b) => b.ts - a.ts)
|
||||
return sorted[0]?.id
|
||||
}
|
||||
|
||||
export async function run(promptArg: string | undefined, flagOptions: FlagOptions) {
|
||||
setLogger({
|
||||
info: () => {},
|
||||
|
|
@ -360,6 +327,18 @@ export async function run(promptArg: string | undefined, flagOptions: FlagOption
|
|||
}
|
||||
|
||||
const useStdinPromptStream = flagOptions.stdinPromptStream
|
||||
let resolvedResumeSessionId: string | undefined
|
||||
|
||||
if (isResumeRequested) {
|
||||
const workspaceSessions = await readWorkspaceTaskSessions(effectiveWorkspacePath)
|
||||
try {
|
||||
resolvedResumeSessionId = resolveWorkspaceResumeSessionId(workspaceSessions, requestedSessionId)
|
||||
} catch (error) {
|
||||
const message = error instanceof Error ? error.message : String(error)
|
||||
console.error(`[CLI] Error: ${message}`)
|
||||
process.exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
if (!isTuiEnabled) {
|
||||
if (!prompt && !useStdinPromptStream && !isResumeRequested) {
|
||||
|
|
@ -394,8 +373,8 @@ export async function run(promptArg: string | undefined, flagOptions: FlagOption
|
|||
createElement(App, {
|
||||
...extensionHostOptions,
|
||||
initialPrompt: prompt,
|
||||
initialSessionId: requestedSessionId,
|
||||
continueSession: shouldContinueSession,
|
||||
initialSessionId: resolvedResumeSessionId,
|
||||
continueSession: false,
|
||||
version: VERSION,
|
||||
createExtensionHost: (opts: ExtensionHostOptions) => new ExtensionHost(opts),
|
||||
}),
|
||||
|
|
@ -422,16 +401,6 @@ export async function run(promptArg: string | undefined, flagOptions: FlagOption
|
|||
let keepAliveInterval: NodeJS.Timeout | undefined
|
||||
let isShuttingDown = false
|
||||
let hostDisposed = false
|
||||
let taskHistorySnapshot: HistoryItem[] = []
|
||||
|
||||
const onExtensionMessage = (message: unknown) => {
|
||||
const taskHistory = extractTaskHistoryFromMessage(message)
|
||||
if (taskHistory) {
|
||||
taskHistorySnapshot = taskHistory
|
||||
}
|
||||
}
|
||||
|
||||
host.on("extensionWebviewMessage", onExtensionMessage)
|
||||
|
||||
const jsonEmitter = useJsonOutput
|
||||
? new JsonEventEmitter({
|
||||
|
|
@ -497,7 +466,6 @@ export async function run(promptArg: string | undefined, flagOptions: FlagOption
|
|||
}
|
||||
|
||||
hostDisposed = true
|
||||
host.off("extensionWebviewMessage", onExtensionMessage)
|
||||
jsonEmitter?.detach()
|
||||
await host.dispose()
|
||||
}
|
||||
|
|
@ -594,22 +562,7 @@ export async function run(promptArg: string | undefined, flagOptions: FlagOption
|
|||
}
|
||||
|
||||
if (isResumeRequested) {
|
||||
const resolvedSessionId =
|
||||
requestedSessionId ||
|
||||
getMostRecentTaskIdInWorkspace(taskHistorySnapshot, effectiveWorkspacePath)
|
||||
|
||||
if (requestedSessionId && taskHistorySnapshot.length > 0) {
|
||||
const hasRequestedTask = taskHistorySnapshot.some((item) => item.id === requestedSessionId)
|
||||
if (!hasRequestedTask) {
|
||||
throw new Error(`Session not found in task history: ${requestedSessionId}`)
|
||||
}
|
||||
}
|
||||
|
||||
if (!resolvedSessionId) {
|
||||
throw new Error("No previous tasks found to continue in this workspace.")
|
||||
}
|
||||
|
||||
await bootstrapResumeForStdinStream(host, resolvedSessionId)
|
||||
await bootstrapResumeForStdinStream(host, resolvedResumeSessionId!)
|
||||
}
|
||||
|
||||
await runStdinStreamMode({
|
||||
|
|
@ -621,22 +574,7 @@ export async function run(promptArg: string | undefined, flagOptions: FlagOption
|
|||
})
|
||||
} else {
|
||||
if (isResumeRequested) {
|
||||
const resolvedSessionId =
|
||||
requestedSessionId ||
|
||||
getMostRecentTaskIdInWorkspace(taskHistorySnapshot, effectiveWorkspacePath)
|
||||
|
||||
if (requestedSessionId && taskHistorySnapshot.length > 0) {
|
||||
const hasRequestedTask = taskHistorySnapshot.some((item) => item.id === requestedSessionId)
|
||||
if (!hasRequestedTask) {
|
||||
throw new Error(`Session not found in task history: ${requestedSessionId}`)
|
||||
}
|
||||
}
|
||||
|
||||
if (!resolvedSessionId) {
|
||||
throw new Error("No previous tasks found to continue in this workspace.")
|
||||
}
|
||||
|
||||
await host.resumeTask(resolvedSessionId)
|
||||
await host.resumeTask(resolvedResumeSessionId!)
|
||||
} else {
|
||||
await host.runTask(prompt!)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -20,6 +20,8 @@ program
|
|||
.name("roo")
|
||||
.description("Roo Code CLI - starts an interactive session by default, use -p/--print for non-interactive output")
|
||||
.version(VERSION)
|
||||
.enablePositionalOptions()
|
||||
.passThroughOptions()
|
||||
|
||||
program
|
||||
.argument("[prompt]", "Your prompt")
|
||||
|
|
@ -65,7 +67,11 @@ program
|
|||
)
|
||||
.action(run)
|
||||
|
||||
const listCommand = program.command("list").description("List commands, modes, models, or sessions")
|
||||
const listCommand = program
|
||||
.command("list")
|
||||
.description("List commands, modes, models, or sessions")
|
||||
.enablePositionalOptions()
|
||||
.passThroughOptions()
|
||||
|
||||
const applyListOptions = (command: Command) =>
|
||||
command
|
||||
|
|
|
|||
75
apps/cli/src/lib/task-history/__tests__/index.test.ts
Normal file
75
apps/cli/src/lib/task-history/__tests__/index.test.ts
Normal file
|
|
@ -0,0 +1,75 @@
|
|||
import { readTaskSessionsFromStoragePath } from "@roo-code/core/cli"
|
||||
|
||||
import {
|
||||
filterSessionsForWorkspace,
|
||||
getDefaultCliTaskStoragePath,
|
||||
readWorkspaceTaskSessions,
|
||||
resolveWorkspaceResumeSessionId,
|
||||
} from "../index.js"
|
||||
|
||||
vi.mock("@roo-code/core/cli", async (importOriginal) => {
|
||||
const actual = await importOriginal<typeof import("@roo-code/core/cli")>()
|
||||
return {
|
||||
...actual,
|
||||
readTaskSessionsFromStoragePath: vi.fn(),
|
||||
}
|
||||
})
|
||||
|
||||
describe("task history workspace helpers", () => {
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks()
|
||||
})
|
||||
|
||||
it("filters sessions to the current workspace and sorts newest first", () => {
|
||||
const result = filterSessionsForWorkspace(
|
||||
[
|
||||
{ id: "a", task: "A", ts: 10, workspace: "/workspace/project" },
|
||||
{ id: "b", task: "B", ts: 30, workspace: "/workspace/project/" },
|
||||
{ id: "c", task: "C", ts: 20, workspace: "/workspace/other" },
|
||||
{ id: "d", task: "D", ts: 40 },
|
||||
],
|
||||
"/workspace/project",
|
||||
)
|
||||
|
||||
expect(result.map((session) => session.id)).toEqual(["b", "a"])
|
||||
})
|
||||
|
||||
it("reads from storage path and applies workspace filtering", async () => {
|
||||
vi.mocked(readTaskSessionsFromStoragePath).mockResolvedValue([
|
||||
{ id: "a", task: "A", ts: 10, workspace: "/workspace/project" },
|
||||
{ id: "b", task: "B", ts: 30, workspace: "/workspace/other" },
|
||||
])
|
||||
|
||||
const result = await readWorkspaceTaskSessions("/workspace/project", "/custom/storage")
|
||||
|
||||
expect(readTaskSessionsFromStoragePath).toHaveBeenCalledWith("/custom/storage")
|
||||
expect(result).toEqual([{ id: "a", task: "A", ts: 10, workspace: "/workspace/project" }])
|
||||
})
|
||||
|
||||
it("returns the expected default CLI storage path", () => {
|
||||
expect(getDefaultCliTaskStoragePath()).toContain(".vscode-mock")
|
||||
expect(getDefaultCliTaskStoragePath()).toContain("global-storage")
|
||||
})
|
||||
|
||||
it("resolves explicit session id only when it exists in current workspace sessions", () => {
|
||||
const sessions = [
|
||||
{ id: "a", task: "A", ts: 10, workspace: "/workspace/project" },
|
||||
{ id: "b", task: "B", ts: 20, workspace: "/workspace/project" },
|
||||
]
|
||||
|
||||
expect(resolveWorkspaceResumeSessionId(sessions, "a")).toBe("a")
|
||||
expect(() => resolveWorkspaceResumeSessionId(sessions, "missing")).toThrow(
|
||||
"Session not found in current workspace",
|
||||
)
|
||||
})
|
||||
|
||||
it("resolves continue to most recent session and errors when no sessions exist", () => {
|
||||
const sessions = [
|
||||
{ id: "newer", task: "Newer", ts: 30, workspace: "/workspace/project" },
|
||||
{ id: "older", task: "Older", ts: 10, workspace: "/workspace/project" },
|
||||
]
|
||||
|
||||
expect(resolveWorkspaceResumeSessionId(sessions)).toBe("newer")
|
||||
expect(() => resolveWorkspaceResumeSessionId([])).toThrow("No previous tasks found to continue")
|
||||
})
|
||||
})
|
||||
44
apps/cli/src/lib/task-history/index.ts
Normal file
44
apps/cli/src/lib/task-history/index.ts
Normal file
|
|
@ -0,0 +1,44 @@
|
|||
import os from "os"
|
||||
import path from "path"
|
||||
|
||||
import { readTaskSessionsFromStoragePath, type TaskSessionEntry } from "@roo-code/core/cli"
|
||||
|
||||
import { arePathsEqual } from "@/lib/utils/path.js"
|
||||
|
||||
const DEFAULT_CLI_TASK_STORAGE_PATH = path.join(os.homedir(), ".vscode-mock", "global-storage")
|
||||
|
||||
export function getDefaultCliTaskStoragePath(): string {
|
||||
return DEFAULT_CLI_TASK_STORAGE_PATH
|
||||
}
|
||||
|
||||
export function filterSessionsForWorkspace(sessions: TaskSessionEntry[], workspacePath: string): TaskSessionEntry[] {
|
||||
return sessions
|
||||
.filter((session) => typeof session.workspace === "string" && arePathsEqual(session.workspace, workspacePath))
|
||||
.sort((a, b) => b.ts - a.ts)
|
||||
}
|
||||
|
||||
export async function readWorkspaceTaskSessions(
|
||||
workspacePath: string,
|
||||
storagePath = DEFAULT_CLI_TASK_STORAGE_PATH,
|
||||
): Promise<TaskSessionEntry[]> {
|
||||
const sessions = await readTaskSessionsFromStoragePath(storagePath)
|
||||
return filterSessionsForWorkspace(sessions, workspacePath)
|
||||
}
|
||||
|
||||
export function resolveWorkspaceResumeSessionId(sessions: TaskSessionEntry[], requestedSessionId?: string): string {
|
||||
if (requestedSessionId) {
|
||||
const hasRequestedSession = sessions.some((session) => session.id === requestedSessionId)
|
||||
if (!hasRequestedSession) {
|
||||
throw new Error(`Session not found in current workspace: ${requestedSessionId}`)
|
||||
}
|
||||
|
||||
return requestedSessionId
|
||||
}
|
||||
|
||||
const mostRecentSessionId = sessions[0]?.id
|
||||
if (!mostRecentSessionId) {
|
||||
throw new Error("No previous tasks found to continue in this workspace.")
|
||||
}
|
||||
|
||||
return mostRecentSessionId
|
||||
}
|
||||
Loading…
Add table
Reference in a new issue