Class: Insika::Commands::ResumeTask
- Inherits:
-
Object
- Object
- Insika::Commands::ResumeTask
- Defined in:
- lib/insika/commands/resume_task.rb
Overview
Turn command: resumes a task from the latest checkpoint. Crash-recovery and manual resume both use THIS same path (Recovery only discovers and dispatches). Re-executes the entire checkpointed turn; non-idempotent tools that already completed are skipped via the side-effect registry.
Instance Method Summary collapse
- #call(command) ⇒ Object
-
#initialize(profiles:, task_store:, checkpoint_store:, executor:) ⇒ ResumeTask
constructor
A new instance of ResumeTask.
Constructor Details
#initialize(profiles:, task_store:, checkpoint_store:, executor:) ⇒ ResumeTask
Returns a new instance of ResumeTask.
10 11 12 13 14 15 |
# File 'lib/insika/commands/resume_task.rb', line 10 def initialize(profiles:, task_store:, checkpoint_store:, executor:) @profiles = ProfileSource.coerce(profiles) @task_store = task_store @checkpoint_store = checkpoint_store @executor = executor end |
Instance Method Details
#call(command) ⇒ Object
17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 |
# File 'lib/insika/commands/resume_task.rb', line 17 def call(command) task_id = (command.payload[:task_id] || command.payload["task_id"]).to_s raise Insika::ValidationError, "task_id is required" if task_id.empty? task = @task_store.find(task_id) || (raise Insika::NotFoundError, "task '#{task_id}' not found") # IN-PROCESS RESUME: a :paused task whose fiber is STILL alive (blocked # on await) — do NOT re-dispatch (spawn would duplicate the fiber). Just post :resume; # the fiber transitions paused->running and continues. (A live :waiting is resolved # by ApproveAction, not here.) if task.status == :paused && @executor.running?(task_id) @executor.resume_live(task_id) return { task_id: task_id } end # :queued: a turn that was in the SessionActor queue and never # started at crash time (no checkpoint) — recovering = RUN from scratch, from the # original Command. Profile comes from the agent in the Command itself. if task.status == :queued @executor.spawn_in_session(task, profile: profile_for(task), resume_from: nil) return { task_id: task_id } end # RE-DISPATCH (crash-resume): no live fiber, re-executes from the checkpoint. # resume requires a checkpoint; without one the task is unrecoverable (Recovery # would already have marked it :failed during the sweep). checkpoint = @checkpoint_store.latest(task_id) || (raise Insika::ValidationError, "task '#{task_id}' has no checkpoint — unrecoverable") check_eligibility!(task) # profile comes from the checkpoint (agent_id): the agent may have been removed from # the config between the crash and boot -> fail loudly and clearly. profile = @profiles[checkpoint.agent_id] || (raise Insika::NotFoundError, "agent '#{checkpoint.agent_id}' not configured") @executor.spawn_in_session(task, profile: profile, resume_from: checkpoint) { task_id: task_id } end |