mirror of
https://github.com/anomalyco/opencode.git
synced 2026-09-01 23:14:31 +00:00
fix(core): preserve active session continuation when moving
This commit is contained in:
parent
1ced747051
commit
9517ff1054
2 changed files with 66 additions and 1 deletions
|
|
@ -468,7 +468,8 @@ const layer = Layer.effect(
|
|||
Effect.gen(function* () {
|
||||
const latest = yield* result.get(input.sessionID)
|
||||
const source = yield* fs.stat(latest.location.directory).pipe(Effect.orElseSucceed(() => undefined))
|
||||
if (!source || source.type !== "Directory") {
|
||||
// Active runners must hand off at a step boundary to retain their continuation.
|
||||
if ((!source || source.type !== "Directory") && !(yield* execution.isActive(input.sessionID))) {
|
||||
const cancellations = (yield* SessionInbox.moveIDs(db, input.sessionID)).map(
|
||||
(item) => [SessionEvent.InboxCancelled, { sessionID: input.sessionID, inboxID: item.id }] as const,
|
||||
)
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@ import { Session } from "@opencode-ai/core/session"
|
|||
import { SessionEvent } from "@opencode-ai/core/session/event"
|
||||
import { SessionExecution } from "@opencode-ai/core/session/execution"
|
||||
import { SessionProjector } from "@opencode-ai/core/session/projector"
|
||||
import { SessionRunner } from "@opencode-ai/core/session/runner/index"
|
||||
import { SessionStore } from "@opencode-ai/core/session/store"
|
||||
import { LayerNode } from "@opencode-ai/util/effect/layer-node"
|
||||
import { tmpdirScoped } from "./fixture/tmpdir"
|
||||
|
|
@ -30,6 +31,34 @@ const it = testEffect(
|
|||
],
|
||||
),
|
||||
)
|
||||
const itWithActiveExecution = testEffect(
|
||||
AppNodeBuilder.build(
|
||||
LayerNode.group([
|
||||
Database.node,
|
||||
Bus.node,
|
||||
SessionProjector.node,
|
||||
SessionStore.node,
|
||||
SessionExecution.node,
|
||||
Session.node,
|
||||
]),
|
||||
[
|
||||
[Project.node, globalProjectNode],
|
||||
[
|
||||
LocationServiceMap.node,
|
||||
Layer.effect(
|
||||
LocationServiceMap.Service,
|
||||
LayerMap.make(
|
||||
(ref: Location.Ref) =>
|
||||
Layer.merge(
|
||||
LayerNode.compile(Location.boundNode(ref), [[Project.node, globalProjectNode]]),
|
||||
Layer.succeed(SessionRunner.Service, { drain: () => Effect.never }),
|
||||
) as unknown as Layer.Layer<LocationServices>,
|
||||
),
|
||||
),
|
||||
],
|
||||
],
|
||||
),
|
||||
)
|
||||
const unavailableLocations = Layer.effect(
|
||||
LocationServiceMap.Service,
|
||||
LayerMap.make(
|
||||
|
|
@ -104,6 +133,41 @@ describe("Session.move", () => {
|
|||
),
|
||||
)
|
||||
|
||||
itWithActiveExecution.live("defers an active move when the source directory no longer exists", () =>
|
||||
tmpdirScoped().pipe(
|
||||
Effect.flatMap((tmp) =>
|
||||
Effect.gen(function* () {
|
||||
const session = yield* Session.Service
|
||||
const execution = yield* SessionExecution.Service
|
||||
const source = AbsolutePath.make(path.join(tmp.path, "source"))
|
||||
const destination = AbsolutePath.make(tmp.path)
|
||||
yield* Effect.promise(() => mkdir(source))
|
||||
const created = yield* session.create({ location: Location.Ref.make({ directory: source }) })
|
||||
|
||||
// Hold real execution open so the move cannot be consumed before admission is checked.
|
||||
yield* execution.wake(created.id)
|
||||
expect(yield* execution.isActive(created.id)).toBe(true)
|
||||
yield* Effect.promise(() => rm(source, { recursive: true }))
|
||||
|
||||
yield* session.move({ sessionID: created.id, directory: destination })
|
||||
|
||||
expect((yield* session.get(created.id)).location.directory).toBe(source)
|
||||
expect(yield* session.inbox(created.id)).toMatchObject([
|
||||
{
|
||||
type: "move",
|
||||
delivery: "steer",
|
||||
payload: { location: { directory: destination } },
|
||||
},
|
||||
])
|
||||
expect(yield* execution.isActive(created.id)).toBe(true)
|
||||
|
||||
yield* execution.interrupt(created.id)
|
||||
yield* execution.awaitIdle(created.id)
|
||||
}),
|
||||
),
|
||||
),
|
||||
)
|
||||
|
||||
it.effect("keeps a moved session out of its former directory's new identity", () =>
|
||||
tmpdirScoped().pipe(
|
||||
Effect.flatMap((tmp) =>
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue