///|
/// Execution lifetime for work submitted through the composed task capability.
pub(all) enum TaskMode {
  Foreground
  Background
} derive(Eq, Debug)

///|
/// A unit of Agent-owned work. The Agent supplies the structured scope and
/// records the terminal outcome; callers never supply a TaskGroup.
pub(all) struct TaskSpec {
  session_id : String
  mode : TaskMode
  label : String
  timeout_ms : Int?
  run : async () -> @kernel.Message
}

///|
/// Stable identity returned at admission. The id is opaque to the caller.
pub(all) struct TaskReceipt {
  id : String
  session_id : String
  mode : TaskMode
  label : String
  extension_id : String
} derive(Eq, Debug)

///|
/// The single terminal state shared by foreground waits and background inbox
/// delivery.
pub(all) enum TaskStatus {
  Completed(message~ : @kernel.Message)
  Failed(reason~ : String)
  TimedOut
  Cancelled(reason~ : String)
} derive(Eq, Debug)

///|
/// A task's terminal result. Background outcomes are retained by session
/// until the owning Agent successfully commits them.
pub(all) struct TaskOutcome {
  receipt : TaskReceipt
  status : TaskStatus
} derive(Eq, Debug)

///|
/// Admission failures are explicit so an extension cannot mistake an
/// inactive scope or a closed Agent for accepted work.
pub(all) enum TaskSubmitError {
  Unavailable
  Closed
  QueueFull
  InvalidSession(reason~ : String)
} derive(Eq, Debug)

///|
/// Opaque handle for one admitted task. The callbacks are installed by core
/// composition; extensions only receive wait/cancel operations.
pub struct TaskHandle {
  priv receipt_ : TaskReceipt
  priv wait_ : async (TaskReceipt) -> TaskOutcome
  priv cancel_ : (TaskReceipt) -> Unit
}

///|
pub fn TaskHandle::receipt(self : TaskHandle) -> TaskReceipt {
  self.receipt_
}

///|
pub async fn TaskHandle::wait(self : TaskHandle) -> TaskOutcome {
  (self.wait_)(self.receipt_)
}

///|
pub fn TaskHandle::cancel(self : TaskHandle) -> Unit {
  (self.cancel_)(self.receipt_)
}

///|
/// Concrete composed capability. It is deliberately a value rather than a
/// public trait: task admission is Agent-owned and has no replacement port.
pub struct Tasks {
  priv submit_ : (TaskSpec) -> Result[TaskHandle, TaskSubmitError]
}

///|
pub fn Tasks::submit(
  self : Tasks,
  spec : TaskSpec,
) -> Result[TaskHandle, TaskSubmitError] {
  (self.submit_)(spec)
}

///|
/// Framework construction hook. Product extensions obtain the value through
/// CompositionView and do not need this constructor.
pub fn Tasks::from_submit(
  submit~ : (TaskSpec) -> Result[TaskHandle, TaskSubmitError],
) -> Tasks {
  { submit_: fn(spec) { submit(spec) }, }
}

///|
/// Framework construction hook for the opaque handle.
pub fn TaskHandle::from_callbacks(
  receipt~ : TaskReceipt,
  wait~ : async (TaskReceipt) -> TaskOutcome,
  cancel~ : (TaskReceipt) -> Unit,
) -> TaskHandle {
  { receipt_: receipt, wait_: wait, cancel_: cancel, }
}