Class: Silas::ToolInvocation
- Inherits:
-
ApplicationRecord
- Object
- ActiveRecord::Base
- ApplicationRecord
- Silas::ToolInvocation
- Includes:
- Inbox::Broadcastable
- Defined in:
- app/models/silas/tool_invocation.rb
Constant Summary collapse
- STATUSES =
%w[pending started completed failed in_doubt].freeze
- EFFECT_MODES =
%w[transactional at_most_once idempotent].freeze
- APPROVAL_STATES =
[ nil, "required", "approved", "declined", "expired" ].freeze
Class Method Summary collapse
-
.expire_stale!(now: Time.current) ⇒ Object
Sweep for the rescuer: expire parked invocations past their TTL and fail their turns (parked-forever ghosts are a bug, not a feature).
Instance Method Summary collapse
-
#approve!(by: nil) ⇒ Object
Approve a parked invocation and resume the turn with a FRESH job (the parked job exited normally; its continuation is consumed).
- #awaiting_approval? ⇒ Boolean
- #completed? ⇒ Boolean
-
#decline!(reason:, by: nil) ⇒ Object
Decline: for an approval gate, eve's shape — the tool is not executed and the model sees reason as the result, then the loop continues.
- #in_doubt? ⇒ Boolean
- #notify_channel_approval ⇒ Object
- #should_notify_approval? ⇒ Boolean
Class Method Details
.expire_stale!(now: Time.current) ⇒ Object
Sweep for the rescuer: expire parked invocations past their TTL and fail their turns (parked-forever ghosts are a bug, not a feature).
55 56 57 58 59 60 61 |
# File 'app/models/silas/tool_invocation.rb', line 55 def self.expire_stale!(now: Time.current) where(approval_state: "required").where(approval_expires_at: ..now).find_each do |inv| inv.update!(approval_state: "expired", status: "failed", result: { "denied" => "approval expired" }) inv.turn.finish!(:failed, reason: "approval_expired") end end |
Instance Method Details
#approve!(by: nil) ⇒ Object
Approve a parked invocation and resume the turn with a FRESH job (the parked job exited normally; its continuation is consumed). For an in-doubt invocation, approval means "it did not run — re-execute".
36 37 38 39 40 |
# File 'app/models/silas/tool_invocation.rb', line 36 def approve!(by: nil) assert_parked! update!(status: "pending", approval_state: "approved", approved_by: by) resume_turn! end |
#awaiting_approval? ⇒ Boolean
19 |
# File 'app/models/silas/tool_invocation.rb', line 19 def awaiting_approval? = approval_state == "required" |
#completed? ⇒ Boolean
17 |
# File 'app/models/silas/tool_invocation.rb', line 17 def completed? = status == "completed" |
#decline!(reason:, by: nil) ⇒ Object
Decline: for an approval gate, eve's shape — the tool is not executed and the model sees reason as the result, then the loop continues. For an in-doubt invocation, decline means "assume it ran / abandon" — the operator-supplied reason becomes the recorded outcome.
46 47 48 49 50 51 |
# File 'app/models/silas/tool_invocation.rb', line 46 def decline!(reason:, by: nil) assert_parked! update!(status: "failed", approval_state: "declined", approved_by: by, decline_reason: reason, result: { "denied" => reason }) resume_turn! end |
#in_doubt? ⇒ Boolean
18 |
# File 'app/models/silas/tool_invocation.rb', line 18 def in_doubt? = status == "in_doubt" |
#notify_channel_approval ⇒ Object
29 30 31 |
# File 'app/models/silas/tool_invocation.rb', line 29 def notify_channel_approval ChannelDeliveryJob.perform_later("approval", id) end |
#should_notify_approval? ⇒ Boolean
25 26 27 |
# File 'app/models/silas/tool_invocation.rb', line 25 def should_notify_approval? saved_change_to_approval_state? && approval_state == "required" && turn.session.channel.present? end |