rubik 0.1.1 → 0.1.2

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 13f2d00bb2c72a92d48741df2cb3c3e84fc970a9ce3e4e844722afa58ef59599
4
- data.tar.gz: f3314db467118bbd56aa79625f954f5c9e76611c56b5eb5e467d442fa440aa63
3
+ metadata.gz: a0ec56cafd854c2e2999c68cdfdf787322ca4d7d2a2866fac4b4e8ce9f1efac7
4
+ data.tar.gz: ec584d8ac5ee2039423d71968cce852afb65fcb072cbdbb87325ef94693e6a5d
5
5
  SHA512:
6
- metadata.gz: a5c29f5af7119d6b9189e37d39390c6dd2463caa1fd870e49022b92a0ed72481b227616000a84a2e201f26c7a65009f7fcb05ac6591db6ca1b9ecae3f81bc6d4
7
- data.tar.gz: 3ab1188c4607f7dbcbe4011111723f530555052497154eaea6c8d47b09dc927bfcfa43b50b88869f2dc8bc1e402c7589a569d59ad584d29b991bfff765292d08
6
+ metadata.gz: 3a535d5c0f45c74363387e7df47da8c87b1296be897fdd09adc17e4adfcc576b48555066cb0b3618d27f1eeeba052c53fe1ccb7a670598a43cad57bb8a4cc942
7
+ data.tar.gz: d75bb114c57fb2221f17f9d89a1a0fe1811b9e36ba52289337030b822210ef5373118e7d8438c4000da9dfca65fb4dadf3b5f85bdb8ad117ee50d56469c1a782
data/ATTRIBUTION.md CHANGED
@@ -54,6 +54,15 @@ SOFTWARE.
54
54
  - **Spatial Composability:** Reactive Coeffect Dependency DAG ($\Sigma \vdash C : \tau$) implemented in [`Rubik::Runtime::ComponentFiber`](lib/rubik/runtime/fiber.rb) and [`Rubik::Runtime::Manager`](lib/rubik/runtime/manager.rb).
55
55
  - Zero-downtime hot reloading, atomic rollback on activation failure, and leak-free lifecycle management.
56
56
 
57
+ ### SKILL.state (Structured Execution State)
58
+ - **Paper:** *"SKILL.state: Scalable Long-Horizon Agent Skills"* ([arXiv:2608.26263](https://arxiv.org/abs/2608.26263))
59
+ - **Authors:** Sanket Badhe, Priyanka Tiwari, Jonghyun Chung
60
+ - **Rubik Implementation:**
61
+ - The structured goal ledger ([`orchestrator/goal_ledger/`](lib/rubik/orchestrator/goal_ledger/), 2026-10-10): the `!goal` loop replaces append-only conversation memory with an explicit, mutable, rev-guarded execution state Σ (objective, tasks, findings, decisions, open questions, environment notes) — the model sees the current state, not an ever-growing history.
62
+ - Validated JSON-patch intake (RFC 6902 subset) with the `:syntax` / `:schema` / `:conflict` rejection taxonomy and bounded-retry model feedback (`GoalLedger::Patch` / `Schema` / `Validator`).
63
+ - The paper's world-drift recovery, coded as tri-state FS re-verification: declarative `verify` assertions are re-checked at step boundaries; a failed assertion flips the task to `stale` with an internal rev bump, so the `:conflict` the model then hits IS the drift signal (`GoalLedger::Verifier` / `VerifySpec` / `DriftNote`).
64
+ - Observation projection: banked tool results compact into per-row `<absorbed_into_ledger>` tombstones while the last K turns stay raw (`GoalLedger::ObservationProjection`).
65
+
57
66
  ---
58
67
 
59
68
  ## 🌳 3. AST Parsing & Multi-Language Ecosystem
@@ -112,7 +121,7 @@ SOFTWARE.
112
121
  | **`strings-ansi`** | Piotr Murach | MIT | ANSI-aware string truncation and alignment for REPL formatters. |
113
122
  | **`reline`** | Hiroshi SHIBATA & Ruby Team | Ruby License / BSD-2-Clause | Line-editing, history navigation, autocompletion hooks, and headless terminal adaptation. |
114
123
  | **`prism`** | Kevin Newton & Ruby Core Team | MIT | High-performance Ruby parsing and AST extraction ([`Rubik::AST::RubyExtractor`](lib/rubik/ast/ruby_extractor.rb)). |
115
- | **`minitest`** | Ryan Davis & seattlerb | MIT | Fast, modular test suite powering Rubik's 1,690+ unit, integration, and contract tests. |
124
+ | **`minitest`** | Ryan Davis & seattlerb | MIT | Fast, modular test suite powering Rubik's 3,400+ unit, integration, and contract tests. |
116
125
  | **`rubocop`** | Bozhidar Batsov & contributors | MIT | Static code analysis, style enforcement, and architectural hygiene gates. |
117
126
  | **`benchmark`, `logger`, `pstore`, `reline`, `fiddle`** | Ruby core / rubygems teams | Ruby License / BSD-2-Clause | Ruby stdlib gems pinned explicitly in `rubik.gemspec` as they are unbundled from the default set in Ruby 3.4/3.5. |
118
127
 
data/CHANGELOG.md CHANGED
@@ -10,8 +10,37 @@ The format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
10
10
 
11
11
  Nothing yet.
12
12
 
13
+ ## [0.1.2] - 2026-10-11
14
+
15
+ The Anthropic correctness release: the provider now speaks the modern
16
+ extended-thinking contract, so pinning any current Claude model works
17
+ instead of failing with HTTP 400.
18
+
19
+ ### Fixed
20
+
21
+ - **Anthropic provider speaks the modern thinking contract:** the
22
+ default model pin moves from the stale `claude-3-7-sonnet-20250219`
23
+ to `claude-sonnet-5-5`, and the extended-thinking payload follows the
24
+ model line ? 4.6+ and the 5.x/Fable models get the adaptive form
25
+ (`thinking: {type: "adaptive"}` with an `output_config` effort),
26
+ 3-7-sonnet through the 4.5 line keep the `budget_tokens` math, and
27
+ pre-thinking lines get nothing attached. 0.1.1 sent the legacy form
28
+ to every Anthropic model, which Claude 4.7+ rejects with HTTP 400.
29
+
30
+ ### Changed
31
+
32
+ - Extended thinking auto-enables on every thinking-capable Anthropic
33
+ model line (previously only claude-3-7-sonnet); `thinking: false`
34
+ still disables it everywhere and `thinking_budget_ratio` shapes the
35
+ legacy form only. Requests that carry thinking still force
36
+ `temperature` to 1.0.
37
+ - README quick start documents the native Anthropic provider
38
+ (`ANTHROPIC_API_KEY` + `!model anthropic/<model>`) next to the
39
+ OpenAI-compatible example.
40
+
13
41
  ## [0.1.1] - 2026-10-11
14
42
 
43
+
15
44
  The Linux surface release: CI now proves Linux on Ruby 3.4 and 4.0
16
45
  (`linux-test` is a required check), and the host seams that surface
17
46
  exposed are fixed. Ships the structured goal ledger, the default-on
data/README.md CHANGED
@@ -11,7 +11,7 @@ English | [Türkçe](README.tr.md)
11
11
  [![Protocol](https://img.shields.io/badge/protocol-AGENTS.md-blue.svg)](AGENTS.md)
12
12
  [![Commands Guide](https://img.shields.io/badge/docs-COMMANDS.md-yellow.svg)](COMMANDS.md)
13
13
  [![Config Guide](https://img.shields.io/badge/docs-CONFIG.md-green.svg)](CONFIG.md)
14
- [![Test Suite](https://img.shields.io/badge/tests-3202%20runs%20%7C%20100%25%20passed-brightgreen.svg)]()
14
+ [![Test Suite](https://img.shields.io/badge/tests-3403%20runs%20%7C%20100%25%20passed-brightgreen.svg)]()
15
15
 
16
16
  ---
17
17
 
@@ -31,6 +31,12 @@ Requires **Ruby 3.4+**:
31
31
  gem install rubik
32
32
  ```
33
33
 
34
+ The installer puts the `rubik` launcher into RubyGems' bin directory (already on your `PATH`), so you can start it from any project directory:
35
+
36
+ ```bash
37
+ rubik
38
+ ```
39
+
34
40
  or with Bundler:
35
41
 
36
42
  ```ruby
@@ -73,6 +79,9 @@ ln -sf $(pwd)/bin/rubik ~/.local/bin/rubik
73
79
  rubik
74
80
  ```
75
81
 
82
+ > [!NOTE]
83
+ > **The gem and a clone can coexist.** On POSIX the symlink always runs your clone: `bin/rubik` resolves `lib/` from its own real path, so a `git pull` is live on the very next launch — even while the `rubik` gem stays installed. The gem-installed `rubik` keeps serving the last released version until you run `gem update rubik`. On Windows, whichever `bin` folder comes first in `PATH` wins — `where.exe rubik` shows the winner.
84
+
76
85
  #### 🪟 Windows (PowerShell / Command Prompt)
77
86
 
78
87
  Rubik ships with built-in `.cmd`, `.bat`, and `.ps1` wrappers for Windows:
@@ -105,6 +114,12 @@ rubik > !provider add deepseek openai_compatible https://api.deepseek.com/v1/cha
105
114
  rubik > !model deepseek/deepseek-coder
106
115
  ```
107
116
 
117
+ Anthropic talks over its **native Messages API** (not an OpenAI shim): the `anthropic` provider is registered out of the box — set `ANTHROPIC_API_KEY` and pick a model:
118
+
119
+ ```bash
120
+ rubik > !model anthropic/claude-sonnet-5-5
121
+ ```
122
+
108
123
  ### First Session Example
109
124
 
110
125
  ```text
@@ -9,7 +9,32 @@ module Rubik
9
9
  class AnthropicProvider < BaseProvider
10
10
  attr_reader :name, :model, :formatter, :headers, :api_key, :api_key_env
11
11
 
12
- def initialize(name: nil, model: "claude-3-7-sonnet-20250219", formatter: AnthropicFormatter.new, headers: {},
12
+ # Extended-thinking support by model line (Anthropic docs, 2026-10):
13
+ # :adaptive — 4.6+ and the 5.x/Fable lines: `thinking: {type: "adaptive"}`
14
+ # with an `output_config` effort; the legacy budget_tokens form is
15
+ # REJECTED with a 400 from 4.7 onward.
16
+ # :legacy — claude-3-7-sonnet through the 4.5 line: the manual
17
+ # `thinking: {type: "enabled", budget_tokens: N}` form is the only
18
+ # accepted mode.
19
+ # nil — 3.5-and-earlier lines accept no thinking at all, and
20
+ # unrecognized names (custom aliases/proxies) get no auto thinking.
21
+ def self.thinking_mode_for(model_name)
22
+ s = model_name.to_s
23
+ return :legacy if s.include?("claude-3-7-sonnet")
24
+ return nil if s.match?(/\Aclaude-[23]/) # number-first 2.x/3.x lines (3-7 handled above)
25
+
26
+ # Single-digit captures: Anthropic versions are one digit per segment,
27
+ # so a snapshot date suffix (claude-sonnet-4-20250514) can never be
28
+ # swallowed as the minor version.
29
+ version = s.match(/\Aclaude-(?:sonnet|opus|haiku|fable)-(\d)(?:-(\d))?/)
30
+ return nil unless version
31
+
32
+ major = version[1].to_i
33
+ minor = version[2].to_i
34
+ major > 4 || (major == 4 && minor >= 6) ? :adaptive : :legacy
35
+ end
36
+
37
+ def initialize(name: nil, model: "claude-sonnet-5-5", formatter: AnthropicFormatter.new, headers: {},
13
38
  api_key: nil, api_key_env: nil)
14
39
  super()
15
40
  @name = name.to_s.downcase
@@ -170,14 +195,25 @@ module Rubik
170
195
  thinking_opt = opts[:thinking]
171
196
  thinking_disabled = [false, :disabled].include?(thinking_opt)
172
197
  ratio = (opts[:thinking_budget_ratio] || 0.5).to_f
198
+ # Thinking is on for every model line that supports it unless
199
+ # explicitly disabled; thinking_budget_ratio only shapes the legacy
200
+ # budget form — adaptive models reason as they see fit.
201
+ mode = self.class.thinking_mode_for(target_model)
173
202
 
174
203
  if thinking_opt.is_a?(Hash)
175
204
  payload[:thinking] = thinking_opt
176
- elsif !thinking_disabled && (thinking_opt == true || target_model.to_s.include?("claude-3-7-sonnet"))
177
- if max_tokens > 2048
178
- budget = [(max_tokens * ratio).to_i, 1024].max
179
- budget = [budget, max_tokens - 1024].min
180
- payload[:thinking] = { type: "enabled", budget_tokens: budget } if budget >= 1024
205
+ elsif !thinking_disabled && (thinking_opt == true || mode)
206
+ if mode == :legacy
207
+ if max_tokens > 2048
208
+ budget = [(max_tokens * ratio).to_i, 1024].max
209
+ budget = [budget, max_tokens - 1024].min
210
+ payload[:thinking] = { type: "enabled", budget_tokens: budget } if budget >= 1024
211
+ end
212
+ else
213
+ # Adaptive models reject the legacy form outright, and for
214
+ # unrecognized names adaptive is the modern-generation default.
215
+ payload[:thinking] = { type: "adaptive" }
216
+ payload[:output_config] = { effort: "high" }
181
217
  end
182
218
  end
183
219
 
@@ -38,7 +38,7 @@ module Rubik
38
38
  @providers = {
39
39
  "ollama" => OllamaProvider.new(model: "qwen2.5-coder:7b", headers: global_headers),
40
40
  "openai" => OpenAIProvider.new(model: "gpt-4o", headers: global_headers),
41
- "anthropic" => AnthropicProvider.new(model: "claude-3-7-sonnet-20250219", headers: global_headers)
41
+ "anthropic" => AnthropicProvider.new(model: "claude-sonnet-5-5", headers: global_headers)
42
42
  }
43
43
  @models_catalog = {}
44
44
  load_from_config(config) if config
@@ -82,7 +82,7 @@ module Rubik
82
82
  end
83
83
  if @providers["anthropic"].instance_of?(AnthropicProvider)
84
84
  @providers["anthropic"] = AnthropicProvider.new(
85
- model: @providers["anthropic"]&.model || "claude-3-7-sonnet-20250219",
85
+ model: @providers["anthropic"]&.model || "claude-sonnet-5-5",
86
86
  headers: global_headers
87
87
  )
88
88
  end
@@ -132,7 +132,7 @@ module Rubik
132
132
  when "anthropic"
133
133
  AnthropicProvider.new(
134
134
  name: p_name,
135
- model: default_model || "claude-3-7-sonnet-20250219",
135
+ model: default_model || "claude-sonnet-5-5",
136
136
  # Honor the same api_key / api_key_env that
137
137
  # openai-compatible specs already forward;
138
138
  # otherwise Anthropic auth fails silently.
@@ -257,7 +257,7 @@ module Rubik
257
257
  when "anthropic"
258
258
  anthropic_headers = (custom.respond_to?(:headers) ? custom.headers : nil) || global_headers
259
259
  AnthropicProvider.new(
260
- model: model_name || "claude-3-7-sonnet-20250219",
260
+ model: model_name || "claude-sonnet-5-5",
261
261
  headers: anthropic_headers,
262
262
  api_key: custom.respond_to?(:api_key) ? custom.api_key : nil,
263
263
  api_key_env: custom.respond_to?(:api_key_env) ? custom.api_key_env : nil
data/lib/rubik/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Rubik
4
- VERSION = "0.1.1"
4
+ VERSION = "0.1.2"
5
5
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rubik
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.1
4
+ version: 0.1.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Özgür Ulukır