refactor(core): document task result correlation boundary
This commit is contained in:
parent
73be8f7658
commit
dc02446ff4
2 changed files with 28 additions and 30 deletions
|
|
@ -50,8 +50,9 @@ export const make = Effect.fn("TaskTool.make")(function* (
|
||||||
model: agent.model ?? parent.model,
|
model: agent.model ?? parent.model,
|
||||||
})
|
})
|
||||||
|
|
||||||
// TODO: Replace this fresh-child composition with Session.run, preserving admission/execution
|
// TODO: Replace this fresh-child-only composition once Session execution exposes a bounded
|
||||||
// separation while returning the assistant response after the admitted boundary.
|
// activity/result identity. An admission ID alone cannot correlate a response when one drain
|
||||||
|
// processes later queued work.
|
||||||
const run = Effect.gen(function* () {
|
const run = Effect.gen(function* () {
|
||||||
yield* sessions.prompt({
|
yield* sessions.prompt({
|
||||||
sessionID: child.id,
|
sessionID: child.id,
|
||||||
|
|
|
||||||
|
|
@ -64,9 +64,9 @@ describe("TaskTool", () => {
|
||||||
const inputs: Parameters<SessionV2.Interface["prompt"]>[0][] = []
|
const inputs: Parameters<SessionV2.Interface["prompt"]>[0][] = []
|
||||||
let resumed = 0
|
let resumed = 0
|
||||||
const sessions = mockSessions({
|
const sessions = mockSessions({
|
||||||
prompt: (input) => {
|
prompt: (value) => {
|
||||||
inputs.push(input)
|
inputs.push(value)
|
||||||
return Effect.succeed(admission(input))
|
return Effect.succeed(admission(value))
|
||||||
},
|
},
|
||||||
resume: () => Effect.sync(() => resumed++),
|
resume: () => Effect.sync(() => resumed++),
|
||||||
})
|
})
|
||||||
|
|
@ -90,8 +90,6 @@ describe("TaskTool", () => {
|
||||||
created = true
|
created = true
|
||||||
return child
|
return child
|
||||||
}),
|
}),
|
||||||
prompt: (input) => Effect.succeed(admission(input)),
|
|
||||||
resume: () => Effect.void,
|
|
||||||
})
|
})
|
||||||
const tool = yield* TaskTool.make(sessions, () => Effect.succeed(undefined))
|
const tool = yield* TaskTool.make(sessions, () => Effect.succeed(undefined))
|
||||||
|
|
||||||
|
|
@ -102,24 +100,6 @@ describe("TaskTool", () => {
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
|
|
||||||
it.live("interrupts the child when foreground waiting is interrupted", () =>
|
|
||||||
Effect.gen(function* () {
|
|
||||||
const resumed = yield* Deferred.make<void>()
|
|
||||||
const interrupts: SessionV2.ID[] = []
|
|
||||||
const sessions = mockSessions({
|
|
||||||
prompt: (input) => Effect.succeed(admission(input)),
|
|
||||||
resume: () => Deferred.succeed(resumed, undefined).pipe(Effect.andThen(Effect.never)),
|
|
||||||
interrupt: (sessionID) => Effect.sync(() => interrupts.push(sessionID)),
|
|
||||||
})
|
|
||||||
const tool = yield* TaskTool.make(sessions, resolveAgent)
|
|
||||||
const fiber = yield* execute(tool, input, "call_task_interrupt").pipe(Effect.forkChild)
|
|
||||||
|
|
||||||
yield* Deferred.await(resumed)
|
|
||||||
yield* Fiber.interrupt(fiber)
|
|
||||||
expect(interrupts).toEqual([childID])
|
|
||||||
}),
|
|
||||||
)
|
|
||||||
|
|
||||||
it.live("does not notify the parent when background work is interrupted", () =>
|
it.live("does not notify the parent when background work is interrupted", () =>
|
||||||
Effect.gen(function* () {
|
Effect.gen(function* () {
|
||||||
let notified = false
|
let notified = false
|
||||||
|
|
@ -140,17 +120,34 @@ describe("TaskTool", () => {
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
it.live("interrupts the child when foreground waiting is interrupted", () =>
|
||||||
|
Effect.gen(function* () {
|
||||||
|
const resumed = yield* Deferred.make<void>()
|
||||||
|
const interrupts: SessionV2.ID[] = []
|
||||||
|
const sessions = mockSessions({
|
||||||
|
resume: () => Deferred.succeed(resumed, undefined).pipe(Effect.andThen(Effect.never)),
|
||||||
|
interrupt: (sessionID) => Effect.sync(() => interrupts.push(sessionID)),
|
||||||
|
})
|
||||||
|
const tool = yield* TaskTool.make(sessions, resolveAgent)
|
||||||
|
const fiber = yield* execute(tool, input, "call_task_interrupt").pipe(Effect.forkChild)
|
||||||
|
|
||||||
|
yield* Deferred.await(resumed)
|
||||||
|
yield* Fiber.interrupt(fiber)
|
||||||
|
expect(interrupts).toEqual([childID])
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
|
||||||
it.live("returns before background completion and steers the result into the parent", () =>
|
it.live("returns before background completion and steers the result into the parent", () =>
|
||||||
Effect.gen(function* () {
|
Effect.gen(function* () {
|
||||||
const gate = yield* Deferred.make<void>()
|
const gate = yield* Deferred.make<void>()
|
||||||
const notified = yield* Deferred.make<Parameters<SessionV2.Interface["prompt"]>[0]>()
|
const notified = yield* Deferred.make<Parameters<SessionV2.Interface["prompt"]>[0]>()
|
||||||
const inputs: Parameters<SessionV2.Interface["prompt"]>[0][] = []
|
const inputs: Parameters<SessionV2.Interface["prompt"]>[0][] = []
|
||||||
const sessions = mockSessions({
|
const sessions = mockSessions({
|
||||||
prompt: (input) => {
|
prompt: (value) => {
|
||||||
inputs.push(input)
|
inputs.push(value)
|
||||||
return input.sessionID === parentID
|
return value.sessionID === parentID
|
||||||
? Deferred.succeed(notified, input).pipe(Effect.as(admission(input)))
|
? Deferred.succeed(notified, value).pipe(Effect.as(admission(value)))
|
||||||
: Effect.succeed(admission(input))
|
: Effect.succeed(admission(value))
|
||||||
},
|
},
|
||||||
resume: () => Deferred.await(gate),
|
resume: () => Deferred.await(gate),
|
||||||
})
|
})
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue