Compare commits

...

2 Commits

Author SHA1 Message Date
Aiden Cline f6b9f8ba75 fix(core): preserve compaction after revert 2026-08-10 20:31:58 +00:00
opencode-agent[bot] d7a7256bb6 test: stabilize Windows CI timing (#41600)
Co-authored-by: Aiden Cline <aidenpcline@gmail.com>
2026-08-10 15:25:55 -05:00
6 changed files with 85 additions and 38 deletions
@@ -11,7 +11,9 @@ import { createAcpFixture, expectOk, initialize, newSession, selectConfigOption
describe("acp lifecycle subprocess", () => {
test("stdin EOF exits cleanly", async () => {
await using fixture = await createAcpFixture()
expect(await fixture.spawn().close()).toBe(0)
const acp = fixture.spawn()
await initialize(acp)
expect(await acp.close()).toBe(0)
}, 60_000)
test("close capability and close request", async () => {
+21 -16
View File
@@ -769,22 +769,27 @@ const layer = Layer.effect(
{ location: current.location },
)
}),
compact: Effect.fn("Session.compact")(function* (input) {
yield* result.get(input.sessionID)
const inputID = input.id ?? SessionMessage.ID.create()
const admitted = yield* SessionPending.admitCompaction(db, bus, {
id: inputID,
sessionID: input.sessionID,
}).pipe(
Effect.catchDefect((defect) =>
defect instanceof SessionPending.LifecycleConflict
? new CompactionConflictError({ sessionID: input.sessionID, inputID })
: Effect.die(defect),
),
)
yield* execution.wake(input.sessionID)
return admitted
}),
compact: Effect.fn("Session.compact")((input) =>
Effect.uninterruptible(
Effect.gen(function* () {
const session = yield* result.get(input.sessionID)
if (session.revert) yield* SessionRevert.commit(session).pipe(Effect.provideService(Bus.Service, bus))
const inputID = input.id ?? SessionMessage.ID.create()
const admitted = yield* SessionPending.admitCompaction(db, bus, {
id: inputID,
sessionID: input.sessionID,
}).pipe(
Effect.catchDefect((defect) =>
defect instanceof SessionPending.LifecycleConflict
? new CompactionConflictError({ sessionID: input.sessionID, inputID })
: Effect.die(defect),
),
)
yield* execution.wake(input.sessionID)
return admitted
}),
),
),
wait: Effect.fn("Session.wait")(function* (sessionID) {
yield* result.get(sessionID)
yield* execution.awaitIdle(sessionID)
+1
View File
@@ -465,6 +465,7 @@ Use native v2 fields.`,
},
}),
)
yield* Effect.yieldNow
yield* Effect.promise(() => fs.writeFile(path.join(directory, "reviewer.md"), "Review once"))
yield* configTest.emitChange({ type: "create", path: path.join(directory, "reviewer.md") })
@@ -185,6 +185,7 @@ Review files`,
},
}),
)
yield* Effect.yieldNow
yield* Effect.promise(() => fs.writeFile(path.join(directory, "review.md"), "Review once"))
yield* configTest.emitChange({ type: "create", path: path.join(directory, "review.md") })
yield* configTest.emitChange({ type: "update", path: path.join(directory, "review.md") })
@@ -118,4 +118,39 @@ describe("Session.compact", () => {
expect((yield* session.context(created.id)).find((message) => message.id === first.id)).toBeUndefined()
}),
)
it.effect("commits a staged revert before admitting manual compaction", () =>
Effect.gen(function* () {
requests = []
const session = yield* Session.Service
const bus = yield* Bus.Service
const created = yield* session.create({ location })
const boundary = SessionMessage.ID.create()
yield* bus.publish(SessionEvent.InputAdmitted, {
sessionID: created.id,
inputID: boundary,
input: {
type: "user",
data: { text: "Discard this turn." },
delivery: "steer",
},
})
yield* bus.publish(SessionEvent.InputPromoted, {
sessionID: created.id,
inputID: boundary,
})
yield* bus.publish(SessionEvent.RevertEvent.Staged, {
sessionID: created.id,
revert: { messageID: boundary, files: [] },
})
const compacted = yield* session.compact({ sessionID: created.id })
expect((yield* session.get(created.id)).revert).toBeUndefined()
expect(yield* session.context(created.id)).toEqual([])
expect(yield* SessionPending.compaction((yield* Database.Service).db, created.id)).toMatchObject({
id: compacted.id,
})
}),
)
})
+24 -21
View File
@@ -286,27 +286,30 @@ describe("ShellTool", () => {
),
)
it.live("permissions compound commands separately", () =>
Effect.acquireUseRelease(
Effect.promise(() => tmpdir()),
(tmp) => {
reset()
return withSession(tmp.path, (registry) =>
executeTool(registry, call({ command: "printf one && printf two" }, "call-compound")),
).pipe(
Effect.andThen(
Effect.sync(() => {
expect(assertions).toHaveLength(1)
expect(assertions[0]).toMatchObject({
resources: ["printf one", "printf two"],
save: ["printf *", "printf *"],
})
}),
),
)
},
(tmp) => Effect.promise(() => tmp[Symbol.asyncDispose]().then(() => undefined)),
),
it.live(
"permissions compound commands separately",
() =>
Effect.acquireUseRelease(
Effect.promise(() => tmpdir()),
(tmp) => {
reset()
return withSession(tmp.path, (registry) =>
executeTool(registry, call({ command: "printf one && printf two" }, "call-compound")),
).pipe(
Effect.andThen(
Effect.sync(() => {
expect(assertions).toHaveLength(1)
expect(assertions[0]).toMatchObject({
resources: ["printf one", "printf two"],
save: ["printf *", "printf *"],
})
}),
),
)
},
(tmp) => Effect.promise(() => tmp[Symbol.asyncDispose]().then(() => undefined)),
),
{ timeout: 15_000 },
)
it.live(