cline-rb 2.1.1 → 2.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +29 -0
- data/README.md +2 -1
- data/lib/cline/cli.rb +1 -1
- data/lib/cline/lenient_schema.rb +116 -0
- data/lib/cline/schema.rb +52 -0
- data/lib/cline/session_message.rb +68 -4
- data/lib/cline/session_messages.rb +26 -0
- data/lib/cline/utils/schema.rb +155 -0
- data/lib/cline/version.rb +1 -1
- metadata +2 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 5df017c7913b29844cc5cdc1611e355bcee832d6f75c09d0765a88eb9ff91b3a
|
|
4
|
+
data.tar.gz: 355b9674891ed24101c3f44d5087edf7060f33f85a8a4b010aedca55014d03b5
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 918c2580895ea328088cf37bd92214c510417a7c457d6fc12e2826a807eaedb6800ae1b24c87064c7103defa61fe9d8ca042f1e8aae0ff0a2aad9bf680167c4e
|
|
7
|
+
data.tar.gz: bd0abcf30c86ed77861df47ab3c46d6ccee9348ff17b5662c431ae9c515f69478f863367e17baf7a12f63535585f246d975c1d87f55cf97e9c5aed0f000ae17f
|
data/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,32 @@
|
|
|
1
|
+
# [v2.3.0](https://github.com/Muriel-Salvan/cline-rb/compare/v2.2.0...v2.3.0) (2026-11-04 15:21:27)
|
|
2
|
+
|
|
3
|
+
## Global changes
|
|
4
|
+
### Patches
|
|
5
|
+
|
|
6
|
+
* [feat(cline): add LenientSchema for tolerant deserialization of session messages's input](https://github.com/Muriel-Salvan/cline-rb/commit/a033d62989c22876030742c5e4979dc7a02bee16)
|
|
7
|
+
|
|
8
|
+
## Changes for cline
|
|
9
|
+
### Features
|
|
10
|
+
|
|
11
|
+
* [feat(cline): add LenientSchema for tolerant deserialization of session messages's input](https://github.com/Muriel-Salvan/cline-rb/commit/a033d62989c22876030742c5e4979dc7a02bee16)
|
|
12
|
+
|
|
13
|
+
## Changes for schema
|
|
14
|
+
### Features
|
|
15
|
+
|
|
16
|
+
* [feat(cline): add LenientSchema for tolerant deserialization of session messages's input](https://github.com/Muriel-Salvan/cline-rb/commit/a033d62989c22876030742c5e4979dc7a02bee16)
|
|
17
|
+
|
|
18
|
+
# [v2.2.0](https://github.com/Muriel-Salvan/cline-rb/compare/v2.1.1...v2.2.0) (2026-11-03 16:44:07)
|
|
19
|
+
|
|
20
|
+
## Global changes
|
|
21
|
+
### Patches
|
|
22
|
+
|
|
23
|
+
* [feat(session-message): add tool input attributes and origin metadata](https://github.com/Muriel-Salvan/cline-rb/commit/b873024a5d85f1032f63af03742e713d84f0818f)
|
|
24
|
+
|
|
25
|
+
## Changes for session-message
|
|
26
|
+
### Features
|
|
27
|
+
|
|
28
|
+
* [feat(session-message): add tool input attributes and origin metadata](https://github.com/Muriel-Salvan/cline-rb/commit/b873024a5d85f1032f63af03742e713d84f0818f)
|
|
29
|
+
|
|
1
30
|
# [v2.1.1](https://github.com/Muriel-Salvan/cline-rb/compare/v2.1.0...v2.1.1) (2026-10-28 09:57:49)
|
|
2
31
|
|
|
3
32
|
## Global changes
|
data/README.md
CHANGED
|
@@ -592,7 +592,7 @@ msg.usage #=> Usage struct (cost, tokens)
|
|
|
592
592
|
msg.to_human #=> human-friendly string (limited to 128 chars by default)
|
|
593
593
|
```
|
|
594
594
|
|
|
595
|
-
Contains nested schemas: `ModelInfo`, `Metrics`, `MessageContent`, `ToolUseInput`.
|
|
595
|
+
Contains nested schemas: `ModelInfo`, `Metrics`, `MessageContent`, `ToolUseInput`, `ToolResultContent`.
|
|
596
596
|
|
|
597
597
|
---
|
|
598
598
|
|
|
@@ -877,6 +877,7 @@ Every domain object (settings, providers, sessions, tasks...) extends `Cline::Sc
|
|
|
877
877
|
- **Declarative attributes** — `attribute :my_field, :string` mirrors the JSON schema.
|
|
878
878
|
- **Automatic camelCase↔snake_case mapping** — Cline stores `autoUpdateEnabled`, Ruby gets `auto_update_enabled`.
|
|
879
879
|
- **Extra attribute preservation** — unknown JSON keys are stored in `extra_attributes` and re-serialized back, maintaining round-trip fidelity.
|
|
880
|
+
- **Typed containers & unions** — `Utils::Schema.collection(Type)` models arrays, `Utils::Schema.map(:string)` dynamic-key maps and `Utils::Schema.union(:string, Utils::Schema.collection(Type))` attributes that can be of several types.
|
|
880
881
|
|
|
881
882
|
3 mixin modules handle file persistence — classes include them to "come alive" from disk:
|
|
882
883
|
|
data/lib/cline/cli.rb
CHANGED
|
@@ -116,7 +116,7 @@ module Cline
|
|
|
116
116
|
# or nil if none (see Session#monitor_message)
|
|
117
117
|
# @param on_question [#call, nil] Callback called for each question that is asked by the assistant, or nil if none.
|
|
118
118
|
# This should be set if an interactive session is expected.
|
|
119
|
-
# If a question is asked without a callback to handle it, an UnexpectedInteractiveSessionError
|
|
119
|
+
# If a question is asked without a callback to handle it, an UnexpectedInteractiveSessionError exception will be raised.
|
|
120
120
|
# - Param question [SessionMessage::MessageContent::ToolUseInput] Question input with possible options
|
|
121
121
|
# (see SessionMessage::MessageContent::ToolUseInput).
|
|
122
122
|
# - Return [String] The answer that the user should provide to this assistant's question.
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
module Cline
|
|
2
|
+
# Base class for schemas whose data may not respect the expected types on deserialization.
|
|
3
|
+
# This is typically the case for attributes filled by an LLM, whose types are not guaranteed
|
|
4
|
+
# by the Cline CLI (for example a String given where an Array is expected).
|
|
5
|
+
#
|
|
6
|
+
# Contrary to {Schema}, deserialization does not raise exceptions for such wrong data:
|
|
7
|
+
# a warning is emitted, every attribute that can be deserialized properly is set on the instance,
|
|
8
|
+
# and the other attributes are stored in {#wrong_attributes}, keyed by their attribute name.
|
|
9
|
+
#
|
|
10
|
+
# @example
|
|
11
|
+
# input = Cline::SessionMessage::MessageContent::ToolUseInput.of_hash(
|
|
12
|
+
# 'question' => 'Continue?',
|
|
13
|
+
# 'files' => 'not-an-array'
|
|
14
|
+
# )
|
|
15
|
+
# input.question #=> 'Continue?'
|
|
16
|
+
# input.files #=> nil
|
|
17
|
+
# input.wrong_attributes #=> { files: 'not-an-array' }
|
|
18
|
+
class LenientSchema < Schema
|
|
19
|
+
extend Utils::Logger
|
|
20
|
+
|
|
21
|
+
# @!group Public API
|
|
22
|
+
|
|
23
|
+
# The attributes that could not be deserialized, keyed by their attribute name, with their raw value.
|
|
24
|
+
# Only set when at least one attribute could not be deserialized.
|
|
25
|
+
# When the whole value is not a Hash, it is stored under the '_raw' key.
|
|
26
|
+
#
|
|
27
|
+
# @return [Hash{Symbol => Object}, nil] The wrong attributes, or nil if deserialization succeeded
|
|
28
|
+
attr_accessor :wrong_attributes
|
|
29
|
+
|
|
30
|
+
class << self
|
|
31
|
+
# @!group Internal
|
|
32
|
+
|
|
33
|
+
# Cast an input value to this Schema object.
|
|
34
|
+
# Any value that cannot be cast is turned into an empty instance with {#wrong_attributes} set,
|
|
35
|
+
# instead of raising an exception.
|
|
36
|
+
#
|
|
37
|
+
# @param value [Schema, Hash, nil] The value that could be used to initialize a new instance of this attribute
|
|
38
|
+
# @return [Schema, nil] The corresponding instance, or nil if none
|
|
39
|
+
def cast(value)
|
|
40
|
+
super
|
|
41
|
+
rescue StandardError => e
|
|
42
|
+
# TODO: Make this a warn message when logger will be implemented properly
|
|
43
|
+
log_debug "!!! Cline: Unable to cast a #{value.class} into a #{name} (#{e.class}: #{e.message}). Raw value: #{value.to_s[0, 256]}"
|
|
44
|
+
instance = new
|
|
45
|
+
instance.wrong_attributes = { '_raw' => value.to_s }
|
|
46
|
+
instance
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
# Parse a Hash object and instantiate the proper instance from it.
|
|
50
|
+
# Instead of raising an exception when an attribute cannot be deserialized
|
|
51
|
+
# (wrong type, wrong structure...), this attribute is left unset and its raw value
|
|
52
|
+
# is stored in {#wrong_attributes}, while a warning is emitted.
|
|
53
|
+
# Attributes that can be deserialized properly are still set on the instance.
|
|
54
|
+
#
|
|
55
|
+
# @param hash [Hash] Data
|
|
56
|
+
# @param args [Array] Remaining arguments to be transferred to Shale
|
|
57
|
+
# @param kwargs [Hash] Remaining kwargs to be transferred to Shale
|
|
58
|
+
# @return [LenientSchema] Corresponding instance
|
|
59
|
+
def of_hash(hash, *args, **kwargs)
|
|
60
|
+
complete_hash_mapping
|
|
61
|
+
instance = new
|
|
62
|
+
unless hash.is_a?(Hash)
|
|
63
|
+
# TODO: Make this a warn message when logger will be implemented properly
|
|
64
|
+
log_debug "Cline: Unable to deserialize a #{name} from a #{hash.class}. Raw value: #{hash.to_s[0, 256]}"
|
|
65
|
+
instance.wrong_attributes = { '_raw' => hash.to_s }
|
|
66
|
+
return instance
|
|
67
|
+
end
|
|
68
|
+
mappings = hash_mapping.keys
|
|
69
|
+
wrong_attributes = {}
|
|
70
|
+
hash.each do |key, value|
|
|
71
|
+
mapping = mappings[key]
|
|
72
|
+
if mapping.nil?
|
|
73
|
+
# Keep unknown attributes for round-trip fidelity, as Schema does
|
|
74
|
+
(instance.extra_attributes ||= {})[key] = value
|
|
75
|
+
next
|
|
76
|
+
end
|
|
77
|
+
begin
|
|
78
|
+
set_attribute_from_hash(instance, mapping, value, *args, **kwargs)
|
|
79
|
+
rescue StandardError => e
|
|
80
|
+
# TODO: Make this a warn message when logger will be implemented properly
|
|
81
|
+
log_debug "Cline: Unable to deserialize attribute #{key.inspect} of #{name} (#{e.class}: #{e.message}). Raw value: #{value.to_s[0, 256]}"
|
|
82
|
+
wrong_attributes[mapping.attribute] = value
|
|
83
|
+
end
|
|
84
|
+
end
|
|
85
|
+
instance.wrong_attributes = wrong_attributes unless wrong_attributes.empty?
|
|
86
|
+
instance
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
private
|
|
90
|
+
|
|
91
|
+
# Set one attribute of an instance from its Hash value, mirroring Shale's own of_hash logic.
|
|
92
|
+
#
|
|
93
|
+
# @param instance [LenientSchema] The instance to set the attribute on
|
|
94
|
+
# @param mapping [Shale::Mapping::Descriptor::Dict] The hash mapping of the attribute
|
|
95
|
+
# @param value [Object] The raw value to deserialize
|
|
96
|
+
# @param args [Array] Remaining arguments to be transferred to Shale
|
|
97
|
+
# @param kwargs [Hash] Remaining kwargs to be transferred to Shale
|
|
98
|
+
# @return [void]
|
|
99
|
+
def set_attribute_from_hash(instance, mapping, value, *args, **kwargs)
|
|
100
|
+
attribute = attributes[mapping.attribute]
|
|
101
|
+
check_scalar_value!(attribute, value)
|
|
102
|
+
casted_value =
|
|
103
|
+
if value.nil?
|
|
104
|
+
nil
|
|
105
|
+
elsif attribute.collection?
|
|
106
|
+
(value.is_a?(Array) ? value : [value]).map do |element|
|
|
107
|
+
element.nil? ? nil : attribute.type.cast(attribute.type.of_hash(element, *args, **kwargs))
|
|
108
|
+
end
|
|
109
|
+
else
|
|
110
|
+
attribute.type.cast(attribute.type.of_hash(value, *args, **kwargs))
|
|
111
|
+
end
|
|
112
|
+
instance.send(attribute.setter, casted_value)
|
|
113
|
+
end
|
|
114
|
+
end
|
|
115
|
+
end
|
|
116
|
+
end
|
data/lib/cline/schema.rb
CHANGED
|
@@ -10,6 +10,8 @@ module Cline
|
|
|
10
10
|
class Schema < Shale::Mapper
|
|
11
11
|
# @!group Internal
|
|
12
12
|
|
|
13
|
+
include Utils::Logger
|
|
14
|
+
|
|
13
15
|
# @return [Hash] Store all extra values from a JSON parse
|
|
14
16
|
attr_accessor :extra_attributes
|
|
15
17
|
|
|
@@ -23,6 +25,31 @@ module Cline
|
|
|
23
25
|
@snake_attributes.uniq!
|
|
24
26
|
end
|
|
25
27
|
|
|
28
|
+
# Enable strict type checks on deserialization.
|
|
29
|
+
# When enabled, a scalar attribute that receives an Array or a Hash raises a TypeError
|
|
30
|
+
# instead of being silently coerced.
|
|
31
|
+
#
|
|
32
|
+
# @return [void]
|
|
33
|
+
def strict_types!
|
|
34
|
+
@strict_types = true
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
# Are strict type checks enabled on deserialization?
|
|
38
|
+
#
|
|
39
|
+
# @return [Boolean] true if strict type checks are enabled
|
|
40
|
+
def strict_types?
|
|
41
|
+
@strict_types == true
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
# Check if a raw value can be deserialized by this schema.
|
|
45
|
+
# By default a schema is deserialized from a Hash, or from an instance of itself.
|
|
46
|
+
#
|
|
47
|
+
# @param raw_value [Object] The raw value to check
|
|
48
|
+
# @return [Boolean] true if {of_hash} can deserialize this value
|
|
49
|
+
def accepts_value?(raw_value)
|
|
50
|
+
raw_value.is_a?(self) || raw_value.is_a?(Hash)
|
|
51
|
+
end
|
|
52
|
+
|
|
26
53
|
# Parse a Hash object and instantiate the proper instance from it.
|
|
27
54
|
#
|
|
28
55
|
# @param hash [Hash] Data
|
|
@@ -42,6 +69,7 @@ module Cline
|
|
|
42
69
|
extra_hash[key] = value
|
|
43
70
|
end
|
|
44
71
|
end
|
|
72
|
+
validate_scalar_types!(known_hash) if strict_types?
|
|
45
73
|
# Give Shale the data it knows about, without extra attributes
|
|
46
74
|
instance = super(known_hash, *args, **kwargs)
|
|
47
75
|
instance.extra_attributes = extra_hash unless extra_hash.empty?
|
|
@@ -81,6 +109,30 @@ module Cline
|
|
|
81
109
|
|
|
82
110
|
private
|
|
83
111
|
|
|
112
|
+
# Validate that scalar attributes do not receive a structured value.
|
|
113
|
+
#
|
|
114
|
+
# @param known_hash [Hash] The known attributes to validate
|
|
115
|
+
# @return [void]
|
|
116
|
+
def validate_scalar_types!(known_hash)
|
|
117
|
+
known_hash.each do |key, value|
|
|
118
|
+
attribute = attributes[hash_mapping.keys[key].attribute]
|
|
119
|
+
check_scalar_value!(attribute, value) if attribute
|
|
120
|
+
end
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
# Check that a value is structurally compatible with an attribute.
|
|
124
|
+
# A scalar attribute should not receive an Array or a Hash.
|
|
125
|
+
#
|
|
126
|
+
# @param attribute [Shale::Attribute] The attribute to check
|
|
127
|
+
# @param value [Object] The raw value to check
|
|
128
|
+
# @return [void]
|
|
129
|
+
def check_scalar_value!(attribute, value)
|
|
130
|
+
return if value.nil? || attribute.collection? || attribute.type < Shale::Type::Complex
|
|
131
|
+
return unless value.is_a?(Array) || value.is_a?(Hash)
|
|
132
|
+
|
|
133
|
+
raise TypeError, "Attribute #{attribute.name.inspect} of #{name} expects a scalar value but got a #{value.class}"
|
|
134
|
+
end
|
|
135
|
+
|
|
84
136
|
# Complete the hash mapping to include camelCase to snake_case as the defaults.
|
|
85
137
|
# Do it only once.
|
|
86
138
|
def complete_hash_mapping
|
|
@@ -46,14 +46,65 @@ module Cline
|
|
|
46
46
|
# @!group Public API
|
|
47
47
|
|
|
48
48
|
# An input used for tool use content.
|
|
49
|
-
|
|
49
|
+
# Its values are filled by the LLM, whose types are not guaranteed by the Cline CLI:
|
|
50
|
+
# deserialization is therefore lenient (wrong values do not raise but are stored in {#wrong_attributes}).
|
|
51
|
+
class ToolUseInput < LenientSchema
|
|
50
52
|
# @!group Public API
|
|
51
53
|
|
|
54
|
+
# A file to be read by the read_files tool
|
|
55
|
+
# The path is expected to be a String: a wrong type (e.g. an Array) is reported as a deserialization error.
|
|
56
|
+
class FileInput < Schema
|
|
57
|
+
# @!group Public API
|
|
58
|
+
|
|
59
|
+
strict_types!
|
|
60
|
+
|
|
61
|
+
# @return [String] File path
|
|
62
|
+
attribute :path, :string
|
|
63
|
+
end
|
|
64
|
+
|
|
52
65
|
# @return [String] Question
|
|
53
66
|
attribute :question, :string
|
|
54
67
|
|
|
55
68
|
# @return [Array<String>] List of options for the given question
|
|
56
69
|
attribute :options, Utils::Schema.collection(:string)
|
|
70
|
+
|
|
71
|
+
# @return [String, nil] File path (for editor / read_files tools)
|
|
72
|
+
attribute :path, :string
|
|
73
|
+
|
|
74
|
+
# @return [String, nil] New file content (for editor tool)
|
|
75
|
+
attribute :new_text, :string
|
|
76
|
+
|
|
77
|
+
# @return [String, nil] Old file content (for editor tool)
|
|
78
|
+
attribute :old_text, :string
|
|
79
|
+
|
|
80
|
+
# @return [String, nil] Shell commands to run (for run_commands tool)
|
|
81
|
+
attribute :commands, :string
|
|
82
|
+
|
|
83
|
+
# @return [Array<FileInput>, nil] Files to read (for read_files tool)
|
|
84
|
+
attribute :files, Utils::Schema.collection(FileInput)
|
|
85
|
+
|
|
86
|
+
cline_snake_attributes :new_text, :old_text
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
# A content entry of a tool result that is not a plain String.
|
|
90
|
+
class ToolResultContent < Schema
|
|
91
|
+
# @!group Public API
|
|
92
|
+
|
|
93
|
+
# @return [String, nil] Query or command executed by the tool
|
|
94
|
+
attribute :query, :string
|
|
95
|
+
|
|
96
|
+
# @return [String, nil] Result of the query or command
|
|
97
|
+
attribute :result, :string
|
|
98
|
+
|
|
99
|
+
# @return [Boolean, nil] Whether the query or command succeeded
|
|
100
|
+
attribute :success, :boolean
|
|
101
|
+
|
|
102
|
+
# Get a human-readable representation of this tool result.
|
|
103
|
+
#
|
|
104
|
+
# @return [String] The result of the tool, or its query if there is no result
|
|
105
|
+
def to_s
|
|
106
|
+
result || query || ''
|
|
107
|
+
end
|
|
57
108
|
end
|
|
58
109
|
|
|
59
110
|
# @return [String] Content block type (text, tool_use, tool_result)
|
|
@@ -62,6 +113,9 @@ module Cline
|
|
|
62
113
|
# @return [String, nil] Text content (for type "text")
|
|
63
114
|
attribute :text, :string
|
|
64
115
|
|
|
116
|
+
# @return [String, nil] Thinking content (for type "thinking")
|
|
117
|
+
attribute :thinking, :string
|
|
118
|
+
|
|
65
119
|
# @return [String, nil] Tool use identifier (for type "tool_use")
|
|
66
120
|
attribute :id, :string
|
|
67
121
|
|
|
@@ -74,8 +128,8 @@ module Cline
|
|
|
74
128
|
# @return [String, nil] Tool use identifier this result corresponds to (for type "tool_result")
|
|
75
129
|
attribute :tool_use_id, :string
|
|
76
130
|
|
|
77
|
-
# @return [String, nil] Content of the tool result (for type "tool_result")
|
|
78
|
-
attribute :content, :string
|
|
131
|
+
# @return [String, Array<ToolResultContent>, nil] Content of the tool result (for type "tool_result")
|
|
132
|
+
attribute :content, Utils::Schema.union(:string, Utils::Schema.collection(ToolResultContent))
|
|
79
133
|
|
|
80
134
|
cline_snake_attributes :tool_use_id
|
|
81
135
|
end
|
|
@@ -141,7 +195,7 @@ module Cline
|
|
|
141
195
|
"Assistant: #{first_text}"
|
|
142
196
|
end
|
|
143
197
|
when 'tool'
|
|
144
|
-
"Tool result: #{
|
|
198
|
+
"Tool result: #{human_tool_result}"
|
|
145
199
|
else
|
|
146
200
|
to_s
|
|
147
201
|
end
|
|
@@ -174,5 +228,15 @@ module Cline
|
|
|
174
228
|
def first_text
|
|
175
229
|
content.find { |c| c.type == 'text' }&.text.to_s.gsub("\n", ' ')
|
|
176
230
|
end
|
|
231
|
+
|
|
232
|
+
# Get a human representation of the tool result content of this message.
|
|
233
|
+
#
|
|
234
|
+
# @return [String] The tool result content, as a String
|
|
235
|
+
def human_tool_result
|
|
236
|
+
tool_result = content.find { |c| c.type == 'tool_result' }&.content
|
|
237
|
+
return tool_result.to_s if tool_result.nil? || tool_result.is_a?(String)
|
|
238
|
+
|
|
239
|
+
Array(tool_result).join(' ')
|
|
240
|
+
end
|
|
177
241
|
end
|
|
178
242
|
end
|
|
@@ -7,6 +7,23 @@ module Cline
|
|
|
7
7
|
|
|
8
8
|
# @!group Public API
|
|
9
9
|
|
|
10
|
+
# Origin of the messages (from where they were generated)
|
|
11
|
+
class Origin < Schema
|
|
12
|
+
# @!group Public API
|
|
13
|
+
|
|
14
|
+
# @return [String] Source of the messages (cli or api)
|
|
15
|
+
attribute :source, :string
|
|
16
|
+
|
|
17
|
+
# @return [String] Interaction mode (user or auto)
|
|
18
|
+
attribute :mode, :string
|
|
19
|
+
|
|
20
|
+
# @return [String] Origin session ID
|
|
21
|
+
attribute :session_id, :string
|
|
22
|
+
|
|
23
|
+
# @return [String] Version of the origin source
|
|
24
|
+
attribute :version, :string
|
|
25
|
+
end
|
|
26
|
+
|
|
10
27
|
Serializable::ClineData.include_for(self, proc { |base_dir| "#{File.basename(base_dir)}.messages.json" })
|
|
11
28
|
|
|
12
29
|
# @return [Integer] Schema version
|
|
@@ -21,6 +38,15 @@ module Cline
|
|
|
21
38
|
# @return [String] Session ID
|
|
22
39
|
attribute :session_id, :string
|
|
23
40
|
|
|
41
|
+
# @return [String, nil] Source of the messages (cli or api)
|
|
42
|
+
attribute :source, :string
|
|
43
|
+
|
|
44
|
+
# @return [String, nil] Interaction mode (user or auto)
|
|
45
|
+
attribute :mode, :string
|
|
46
|
+
|
|
47
|
+
# @return [Origin, nil] Origin of the messages
|
|
48
|
+
attribute :origin, Origin
|
|
49
|
+
|
|
24
50
|
# @return [Array<SessionMessage>] Messages of this session
|
|
25
51
|
attribute :messages, Utils::Schema.collection(SessionMessage)
|
|
26
52
|
|
data/lib/cline/utils/schema.rb
CHANGED
|
@@ -74,6 +74,14 @@ module Cline
|
|
|
74
74
|
subclass.value_type = value_type
|
|
75
75
|
end
|
|
76
76
|
|
|
77
|
+
# Check if a raw value can be deserialized by this map.
|
|
78
|
+
#
|
|
79
|
+
# @param raw_value [Object] The raw value to check
|
|
80
|
+
# @return [Boolean] true if this map can deserialize this value
|
|
81
|
+
def accepts_value?(raw_value)
|
|
82
|
+
raw_value.is_a?(self) || raw_value.is_a?(Hash)
|
|
83
|
+
end
|
|
84
|
+
|
|
77
85
|
# Parse a Hash object and instantiate the proper instance from it.
|
|
78
86
|
#
|
|
79
87
|
# @param hash [Hash] Data
|
|
@@ -233,6 +241,14 @@ module Cline
|
|
|
233
241
|
subclass.value_type = value_type
|
|
234
242
|
end
|
|
235
243
|
|
|
244
|
+
# Check if a raw value can be deserialized by this collection.
|
|
245
|
+
#
|
|
246
|
+
# @param raw_value [Object] The raw value to check
|
|
247
|
+
# @return [Boolean] true if this collection can deserialize this value
|
|
248
|
+
def accepts_value?(raw_value)
|
|
249
|
+
raw_value.is_a?(self) || raw_value.is_a?(Array)
|
|
250
|
+
end
|
|
251
|
+
|
|
236
252
|
# Parse a Ruby object and instantiate the proper instance from it.
|
|
237
253
|
#
|
|
238
254
|
# @param hash [Object] Data
|
|
@@ -285,6 +301,145 @@ module Cline
|
|
|
285
301
|
end
|
|
286
302
|
Schema.const_get(external_name)
|
|
287
303
|
end
|
|
304
|
+
|
|
305
|
+
# Provide a class that can be used by Shale for attributes that can be of several types, like this:
|
|
306
|
+
# "content": "Some text" # A String
|
|
307
|
+
# "content": [{ "query": "ls" }, ...] # An Array of structures
|
|
308
|
+
# The first type accepting a value is used to deserialize / serialize it.
|
|
309
|
+
#
|
|
310
|
+
# @param shale_types [Array<Symbol, Class>] The Shale types this attribute can be (at least 2)
|
|
311
|
+
# @return [Class] Class that can be used in an attribute declaration
|
|
312
|
+
def self.union(*shale_types)
|
|
313
|
+
Union.of(*shale_types)
|
|
314
|
+
end
|
|
315
|
+
|
|
316
|
+
# Handle union types: attributes that can be of several types.
|
|
317
|
+
# Used by {Cline::Utils::Schema.union}.
|
|
318
|
+
class Union
|
|
319
|
+
# @!group Internal
|
|
320
|
+
|
|
321
|
+
# Ruby classes that can be deserialized by each scalar Shale type.
|
|
322
|
+
# Shale types not listed here are considered to accept any value.
|
|
323
|
+
SCALAR_VALUE_CLASSES = {
|
|
324
|
+
Shale::Type::String => String,
|
|
325
|
+
Shale::Type::Integer => Integer,
|
|
326
|
+
Shale::Type::Float => Float,
|
|
327
|
+
Shale::Type::Boolean => [TrueClass, FalseClass],
|
|
328
|
+
Shale::Type::Date => Date,
|
|
329
|
+
Shale::Type::Time => Time,
|
|
330
|
+
Shale::Type::Decimal => BigDecimal
|
|
331
|
+
}.freeze
|
|
332
|
+
|
|
333
|
+
class << self
|
|
334
|
+
# Build (or retrieve) the type class for a union of the given types.
|
|
335
|
+
#
|
|
336
|
+
# @param shale_types [Array<Symbol, Class>] The Shale types this union can be (at least 2)
|
|
337
|
+
# @return [Class] Class that can be used in an attribute declaration
|
|
338
|
+
def of(*shale_types)
|
|
339
|
+
raise ArgumentError, "A union type needs at least 2 types, got #{shale_types.size}" if shale_types.size < 2
|
|
340
|
+
|
|
341
|
+
shale_types = shale_types.map { |shale_type| shale_type.is_a?(Symbol) ? Shale::Type.lookup(shale_type) : shale_type }
|
|
342
|
+
# Give a name to this class because Shale needs it to reference it in its accessors.
|
|
343
|
+
# That also allows us to not redefine the same class several times.
|
|
344
|
+
external_name = :"UnionOf#{shale_types.map { |shale_type| shale_type.to_s.gsub(':', '') }.join('Or')}"
|
|
345
|
+
unless Cline::Utils::Schema.const_defined?(external_name)
|
|
346
|
+
union_class = new_union_class
|
|
347
|
+
union_class.union_types = shale_types
|
|
348
|
+
union_class.external_name = external_name
|
|
349
|
+
Cline::Utils::Schema.const_set(external_name, union_class)
|
|
350
|
+
end
|
|
351
|
+
Cline::Utils::Schema.const_get(external_name)
|
|
352
|
+
end
|
|
353
|
+
|
|
354
|
+
# Check if a raw value can be deserialized by a given Shale type.
|
|
355
|
+
# Shale types that don't handle accepted values are considered as scalar types.
|
|
356
|
+
#
|
|
357
|
+
# @param shale_type [Class] The Shale type to check
|
|
358
|
+
# @param raw_value [Object] The raw value to check
|
|
359
|
+
# @return [Boolean] true if the Shale type can deserialize this value
|
|
360
|
+
def accepts_value?(shale_type, raw_value)
|
|
361
|
+
if shale_type.respond_to?(:accepts_value?)
|
|
362
|
+
shale_type.accepts_value?(raw_value)
|
|
363
|
+
else
|
|
364
|
+
raw_value.is_a?(SCALAR_VALUE_CLASSES.fetch(shale_type, Object))
|
|
365
|
+
end
|
|
366
|
+
end
|
|
367
|
+
|
|
368
|
+
private
|
|
369
|
+
|
|
370
|
+
# Create the class implementing a union type.
|
|
371
|
+
#
|
|
372
|
+
# @return [Class] The new union type class
|
|
373
|
+
def new_union_class
|
|
374
|
+
Class.new(Cline::Schema) do
|
|
375
|
+
class << self
|
|
376
|
+
attr_accessor :union_types, :external_name
|
|
377
|
+
|
|
378
|
+
# Check if a raw value can be deserialized by one of this union's types.
|
|
379
|
+
#
|
|
380
|
+
# @param raw_value [Object] The raw value to check
|
|
381
|
+
# @return [Boolean] true if one of this union's types accepts this value
|
|
382
|
+
def accepts_value?(raw_value)
|
|
383
|
+
raw_value.is_a?(self) ||
|
|
384
|
+
union_types.any? { |union_type| Cline::Utils::Schema::Union.accepts_value?(union_type, raw_value) }
|
|
385
|
+
end
|
|
386
|
+
|
|
387
|
+
# Parse a value and instantiate the proper instance from it.
|
|
388
|
+
#
|
|
389
|
+
# @param value [Object] Data
|
|
390
|
+
# @param args [Array] Remaining arguments to be transferred to Shale
|
|
391
|
+
# @param kwargs [Hash] Remaining kwargs to be transferred to Shale
|
|
392
|
+
# @return [Object, nil] Corresponding instance, or nil if value is nil
|
|
393
|
+
def of_hash(value, *args, **kwargs)
|
|
394
|
+
return nil if value.nil?
|
|
395
|
+
|
|
396
|
+
union_type_for(value).of_hash(value, *args, **kwargs)
|
|
397
|
+
end
|
|
398
|
+
|
|
399
|
+
# Get a Ruby object from an instance.
|
|
400
|
+
#
|
|
401
|
+
# @param value [Object] Object to serialize
|
|
402
|
+
# @param args [Array] Remaining arguments to be transferred to Shale
|
|
403
|
+
# @param kwargs [Hash] Remaining kwargs to be transferred to Shale
|
|
404
|
+
# @return [Object, nil] Corresponding Ruby object, or nil if value is nil
|
|
405
|
+
def as_hash(value, *args, **kwargs)
|
|
406
|
+
return nil if value.nil?
|
|
407
|
+
|
|
408
|
+
union_type_for(value).as_hash(value, *args, **kwargs)
|
|
409
|
+
end
|
|
410
|
+
|
|
411
|
+
# Cast an input value to the type matching it.
|
|
412
|
+
#
|
|
413
|
+
# @param value [Object, nil] The value to cast
|
|
414
|
+
# @return [Object, nil] The corresponding instance, or nil if none
|
|
415
|
+
def cast(value)
|
|
416
|
+
return nil if value.nil?
|
|
417
|
+
|
|
418
|
+
union_type_for(value).cast(value)
|
|
419
|
+
end
|
|
420
|
+
|
|
421
|
+
# Return the class name
|
|
422
|
+
#
|
|
423
|
+
# @return [String] The class name
|
|
424
|
+
def to_s
|
|
425
|
+
"::Cline::Utils::Schema::#{@external_name}"
|
|
426
|
+
end
|
|
427
|
+
|
|
428
|
+
private
|
|
429
|
+
|
|
430
|
+
# Get the type that can deserialize / serialize a given value.
|
|
431
|
+
#
|
|
432
|
+
# @param value [Object] The value to find the type of
|
|
433
|
+
# @return [Class] The corresponding type
|
|
434
|
+
def union_type_for(value)
|
|
435
|
+
union_types.find { |union_type| Cline::Utils::Schema::Union.accepts_value?(union_type, value) } ||
|
|
436
|
+
raise("Unable to cast a #{value.class} into #{name}")
|
|
437
|
+
end
|
|
438
|
+
end
|
|
439
|
+
end
|
|
440
|
+
end
|
|
441
|
+
end
|
|
442
|
+
end
|
|
288
443
|
end
|
|
289
444
|
end
|
|
290
445
|
end
|
data/lib/cline/version.rb
CHANGED
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: cline-rb
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 2.
|
|
4
|
+
version: 2.3.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Muriel Salvan
|
|
@@ -187,6 +187,7 @@ files:
|
|
|
187
187
|
- lib/cline/global_state/models.rb
|
|
188
188
|
- lib/cline/global_state/toggles.rb
|
|
189
189
|
- lib/cline/global_state/workspace.rb
|
|
190
|
+
- lib/cline/lenient_schema.rb
|
|
190
191
|
- lib/cline/log.rb
|
|
191
192
|
- lib/cline/logs.rb
|
|
192
193
|
- lib/cline/mcp_settings.rb
|