|
|
@@ -25,6 +25,7 @@ import { SessionCompaction } from "../../src/session/compaction"
|
|
|
import { Instruction } from "../../src/session/instruction"
|
|
|
import { SessionProcessor } from "../../src/session/processor"
|
|
|
import { SessionPrompt } from "../../src/session/prompt"
|
|
|
+import { SessionRunState } from "../../src/session/run-state"
|
|
|
import { MessageID, PartID, SessionID } from "../../src/session/schema"
|
|
|
import { SessionStatus } from "../../src/session/status"
|
|
|
import { Shell } from "../../src/shell/shell"
|
|
|
@@ -143,6 +144,7 @@ const filetime = Layer.succeed(
|
|
|
)
|
|
|
|
|
|
const status = SessionStatus.layer.pipe(Layer.provideMerge(Bus.layer))
|
|
|
+const run = SessionRunState.layer.pipe(Layer.provide(status))
|
|
|
const infra = Layer.mergeAll(NodeFileSystem.layer, CrossSpawnSpawner.defaultLayer)
|
|
|
function makeHttp() {
|
|
|
const deps = Layer.mergeAll(
|
|
|
@@ -174,6 +176,7 @@ function makeHttp() {
|
|
|
return Layer.mergeAll(
|
|
|
TestLLMServer.layer,
|
|
|
SessionPrompt.layer.pipe(
|
|
|
+ Layer.provideMerge(run),
|
|
|
Layer.provideMerge(compact),
|
|
|
Layer.provideMerge(proc),
|
|
|
Layer.provideMerge(registry),
|
|
|
@@ -300,9 +303,10 @@ const addSubtask = (sessionID: SessionID, messageID: MessageID, model = ref) =>
|
|
|
|
|
|
const boot = Effect.fn("test.boot")(function* (input?: { title?: string }) {
|
|
|
const prompt = yield* SessionPrompt.Service
|
|
|
+ const run = yield* SessionRunState.Service
|
|
|
const sessions = yield* Session.Service
|
|
|
const chat = yield* sessions.create(input ?? { title: "Pinned" })
|
|
|
- return { prompt, sessions, chat }
|
|
|
+ return { prompt, run, sessions, chat }
|
|
|
})
|
|
|
|
|
|
// Loop semantics
|
|
|
@@ -800,7 +804,7 @@ it.live("concurrent loop callers get same result", () =>
|
|
|
provideTmpdirInstance(
|
|
|
(dir) =>
|
|
|
Effect.gen(function* () {
|
|
|
- const { prompt, chat } = yield* boot()
|
|
|
+ const { prompt, run, chat } = yield* boot()
|
|
|
yield* seed(chat.id, { finish: "stop" })
|
|
|
|
|
|
const [a, b] = yield* Effect.all([prompt.loop({ sessionID: chat.id }), prompt.loop({ sessionID: chat.id })], {
|
|
|
@@ -809,7 +813,7 @@ it.live("concurrent loop callers get same result", () =>
|
|
|
|
|
|
expect(a.info.id).toBe(b.info.id)
|
|
|
expect(a.info.role).toBe("assistant")
|
|
|
- yield* prompt.assertNotBusy(chat.id)
|
|
|
+ yield* run.assertNotBusy(chat.id)
|
|
|
}),
|
|
|
{ git: true },
|
|
|
),
|
|
|
@@ -913,6 +917,7 @@ it.live(
|
|
|
provideTmpdirServer(
|
|
|
Effect.fnUntraced(function* ({ llm }) {
|
|
|
const prompt = yield* SessionPrompt.Service
|
|
|
+ const run = yield* SessionRunState.Service
|
|
|
const sessions = yield* Session.Service
|
|
|
yield* llm.hang
|
|
|
|
|
|
@@ -922,7 +927,7 @@ it.live(
|
|
|
const fiber = yield* prompt.loop({ sessionID: chat.id }).pipe(Effect.forkChild)
|
|
|
yield* llm.wait(1)
|
|
|
|
|
|
- const exit = yield* prompt.assertNotBusy(chat.id).pipe(Effect.exit)
|
|
|
+ const exit = yield* run.assertNotBusy(chat.id).pipe(Effect.exit)
|
|
|
expect(Exit.isFailure(exit)).toBe(true)
|
|
|
if (Exit.isFailure(exit)) {
|
|
|
expect(Cause.squash(exit.cause)).toBeInstanceOf(Session.BusyError)
|
|
|
@@ -940,11 +945,11 @@ it.live("assertNotBusy succeeds when idle", () =>
|
|
|
provideTmpdirInstance(
|
|
|
(dir) =>
|
|
|
Effect.gen(function* () {
|
|
|
- const prompt = yield* SessionPrompt.Service
|
|
|
+ const run = yield* SessionRunState.Service
|
|
|
const sessions = yield* Session.Service
|
|
|
|
|
|
const chat = yield* sessions.create({})
|
|
|
- const exit = yield* prompt.assertNotBusy(chat.id).pipe(Effect.exit)
|
|
|
+ const exit = yield* run.assertNotBusy(chat.id).pipe(Effect.exit)
|
|
|
expect(Exit.isSuccess(exit)).toBe(true)
|
|
|
}),
|
|
|
{ git: true },
|
|
|
@@ -985,7 +990,7 @@ unix("shell captures stdout and stderr in completed tool output", () =>
|
|
|
provideTmpdirInstance(
|
|
|
(dir) =>
|
|
|
Effect.gen(function* () {
|
|
|
- const { prompt, chat } = yield* boot()
|
|
|
+ const { prompt, run, chat } = yield* boot()
|
|
|
const result = yield* prompt.shell({
|
|
|
sessionID: chat.id,
|
|
|
agent: "build",
|
|
|
@@ -1000,7 +1005,7 @@ unix("shell captures stdout and stderr in completed tool output", () =>
|
|
|
expect(tool.state.output).toContain("err")
|
|
|
expect(tool.state.metadata.output).toContain("out")
|
|
|
expect(tool.state.metadata.output).toContain("err")
|
|
|
- yield* prompt.assertNotBusy(chat.id)
|
|
|
+ yield* run.assertNotBusy(chat.id)
|
|
|
}),
|
|
|
{ git: true, config: cfg },
|
|
|
),
|
|
|
@@ -1010,7 +1015,7 @@ unix("shell completes a fast command on the preferred shell", () =>
|
|
|
provideTmpdirInstance(
|
|
|
(dir) =>
|
|
|
Effect.gen(function* () {
|
|
|
- const { prompt, chat } = yield* boot()
|
|
|
+ const { prompt, run, chat } = yield* boot()
|
|
|
const result = yield* prompt.shell({
|
|
|
sessionID: chat.id,
|
|
|
agent: "build",
|
|
|
@@ -1024,7 +1029,7 @@ unix("shell completes a fast command on the preferred shell", () =>
|
|
|
expect(tool.state.input.command).toBe("pwd")
|
|
|
expect(tool.state.output).toContain(dir)
|
|
|
expect(tool.state.metadata.output).toContain(dir)
|
|
|
- yield* prompt.assertNotBusy(chat.id)
|
|
|
+ yield* run.assertNotBusy(chat.id)
|
|
|
}),
|
|
|
{ git: true, config: cfg },
|
|
|
),
|
|
|
@@ -1034,7 +1039,7 @@ unix("shell lists files from the project directory", () =>
|
|
|
provideTmpdirInstance(
|
|
|
(dir) =>
|
|
|
Effect.gen(function* () {
|
|
|
- const { prompt, chat } = yield* boot()
|
|
|
+ const { prompt, run, chat } = yield* boot()
|
|
|
yield* Effect.promise(() => Bun.write(path.join(dir, "README.md"), "# e2e\n"))
|
|
|
|
|
|
const result = yield* prompt.shell({
|
|
|
@@ -1050,7 +1055,7 @@ unix("shell lists files from the project directory", () =>
|
|
|
expect(tool.state.input.command).toBe("command ls")
|
|
|
expect(tool.state.output).toContain("README.md")
|
|
|
expect(tool.state.metadata.output).toContain("README.md")
|
|
|
- yield* prompt.assertNotBusy(chat.id)
|
|
|
+ yield* run.assertNotBusy(chat.id)
|
|
|
}),
|
|
|
{ git: true, config: cfg },
|
|
|
),
|
|
|
@@ -1060,7 +1065,7 @@ unix("shell captures stderr from a failing command", () =>
|
|
|
provideTmpdirInstance(
|
|
|
(dir) =>
|
|
|
Effect.gen(function* () {
|
|
|
- const { prompt, chat } = yield* boot()
|
|
|
+ const { prompt, run, chat } = yield* boot()
|
|
|
const result = yield* prompt.shell({
|
|
|
sessionID: chat.id,
|
|
|
agent: "build",
|
|
|
@@ -1073,7 +1078,7 @@ unix("shell captures stderr from a failing command", () =>
|
|
|
|
|
|
expect(tool.state.output).toContain("not found")
|
|
|
expect(tool.state.metadata.output).toContain("not found")
|
|
|
- yield* prompt.assertNotBusy(chat.id)
|
|
|
+ yield* run.assertNotBusy(chat.id)
|
|
|
}),
|
|
|
{ git: true, config: cfg },
|
|
|
),
|
|
|
@@ -1198,7 +1203,7 @@ unix(
|
|
|
provideTmpdirInstance(
|
|
|
(dir) =>
|
|
|
Effect.gen(function* () {
|
|
|
- const { prompt, chat } = yield* boot()
|
|
|
+ const { prompt, run, chat } = yield* boot()
|
|
|
|
|
|
const sh = yield* prompt
|
|
|
.shell({ sessionID: chat.id, agent: "build", command: "sleep 30" })
|
|
|
@@ -1209,7 +1214,7 @@ unix(
|
|
|
|
|
|
const status = yield* SessionStatus.Service
|
|
|
expect((yield* status.get(chat.id)).type).toBe("idle")
|
|
|
- const busy = yield* prompt.assertNotBusy(chat.id).pipe(Effect.exit)
|
|
|
+ const busy = yield* run.assertNotBusy(chat.id).pipe(Effect.exit)
|
|
|
expect(Exit.isSuccess(busy)).toBe(true)
|
|
|
|
|
|
const exit = yield* Fiber.await(sh)
|