Port lib/opencode/rails/ source files; strip Rails.event/Rails.error
Eleven source files moved from ajent-rails:lib/opencode/rails/ to
opencode-rails:lib/opencode/ (flat layout — modules are Opencode::*, not
Opencode::Rails::*; matches opencode-ruby).
artifact.rb 63 LOC
exchange.rb 77 LOC
impostor.rb 48 LOC
message_artifacts.rb 133 LOC
sandbox_file.rb 81 LOC
sandbox.rb 71 LOC
session.rb 168 LOC
tool_display.rb 423 LOC
transform.rb 77 LOC
turn.rb 642 LOC
uploaded_files_prompt.rb 85 LOC
----
total 1,868 LOC
Surgical Rails strips:
exchange.rb:
Rails.event.notify(name, payload)
-> Opencode::Instrumentation.instrument(name, payload) { }
message_artifacts.rb (1 call), turn.rb (6 calls):
Rails.error.report(error, **opts)
-> Opencode::ErrorReporter.report(error, **opts)
Comments/docstrings referencing Rails.error.report / Rails.event left
in place — they document how to wire the host adapter.
ActiveSupport core_ext requires expanded in lib/opencode-rails.rb to
cover Numeric#seconds, Hash#deep_stringify_keys, String#squish/truncate,
String#demodulize. Bundle install + smoke load confirms all 12
gem-provided constants resolve cleanly.
This commit is contained in:
63
lib/opencode/artifact.rb
Normal file
63
lib/opencode/artifact.rb
Normal file
@@ -0,0 +1,63 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module Opencode
|
||||
# A file the host wants to attach to an assistant message: filename,
|
||||
# content bytes, MIME type, and an optional trust-metadata hash.
|
||||
#
|
||||
# Artifacts come from two places in the substrate:
|
||||
#
|
||||
# - Opencode::Exchange.tool_artifacts — content lives inside a tool
|
||||
# call's input/metadata (write tool).
|
||||
# - Opencode::SandboxFile#as_artifact — identity conversion of a
|
||||
# sandbox-resident file (the default path for Blackline + Raven).
|
||||
#
|
||||
# Transforms also return Artifacts; that's why FlightResultsTransform
|
||||
# returns one with the host-rendered HTML + trust metadata stamp.
|
||||
#
|
||||
# An Artifact knows how to attach itself to a message, idempotently:
|
||||
# it consults `message.artifacts` to skip if its filename is already
|
||||
# there. The attaching verb belongs to the Artifact (the noun whose
|
||||
# state the verb consults), not to a separate Attacher class.
|
||||
class Artifact
|
||||
attr_reader :filename, :content, :content_type, :metadata
|
||||
|
||||
def initialize(filename:, content:, content_type:, metadata: {})
|
||||
@filename = filename
|
||||
@content = content
|
||||
@content_type = content_type
|
||||
@metadata = metadata
|
||||
end
|
||||
|
||||
# Idempotent attach. Returns true if newly attached, false if the
|
||||
# filename was already present on the message (so callers can count
|
||||
# what they actually persisted vs what was already there).
|
||||
def attach_to(message)
|
||||
return false if already_attached_to?(message)
|
||||
|
||||
message.artifacts.attach(
|
||||
io: StringIO.new(content),
|
||||
filename: filename,
|
||||
content_type: content_type,
|
||||
metadata: metadata
|
||||
)
|
||||
true
|
||||
end
|
||||
|
||||
def already_attached_to?(message)
|
||||
message.artifacts.any? { |a| a.filename.to_s == filename }
|
||||
end
|
||||
|
||||
def ==(other)
|
||||
other.is_a?(Artifact) &&
|
||||
other.filename == filename &&
|
||||
other.content == content &&
|
||||
other.content_type == content_type &&
|
||||
other.metadata == metadata
|
||||
end
|
||||
alias_method :eql?, :==
|
||||
|
||||
def hash
|
||||
[ filename, content, content_type, metadata ].hash
|
||||
end
|
||||
end
|
||||
end
|
||||
Reference in New Issue
Block a user