riffer 0.30.0 → 0.31.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 7557c63ca04fa006d66a61a53bef6b1f7069ebab67439f00104fbb877d1aceda
4
- data.tar.gz: 39bbe45b7e111ae343e32c54b9769db17dd7d8d9b98cd5cb7d42338aa5e4179c
3
+ metadata.gz: e3f857f2ab85b78d91685b84c1d239a0409291c6945f9cc7aae0b84a5dead2a6
4
+ data.tar.gz: 9ae3efd534a2b099de8268e0b0bc89e2c2682d6a7297ada4409a1056203cf9b3
5
5
  SHA512:
6
- metadata.gz: 61694198a05d63d831dca9b4b37e38b6cacbd7b67214fa47c2027a0a7e1636ee07e5f85641f9fe00ee597d66dfdb0a6fccd0e494b6ec670a84a2020907cfdd0b
7
- data.tar.gz: fbff4457d39da36a1c77e9cb7ea86ccb5e96f0887ba982f6a8a960557ee4beceda3a604011fcfd20951f9baedd59eadbf188832cb6bcc8fcf71e256d93e8cdc3
6
+ metadata.gz: bf1b4dbd17abdfb1873f48d7db5b4b13f6ebde5c7e0e40bad73d7e6ca96f51e0d10c1ed9fe50d8be4a0532bcde9d5eb0eebd3ee4d264b195dbbfa12d09e70f9a
7
+ data.tar.gz: 0226c06943e53b5386659917746d1cd256d83861ab340bd0377393d8a45310fd2da60dad090e15a4f6a23dfa1e865b4fdc84f3bd6c1e0df94eafdaf850e4b4ae
@@ -1,3 +1,3 @@
1
1
  {
2
- ".": "0.30.0"
2
+ ".": "0.31.0"
3
3
  }
data/CHANGELOG.md CHANGED
@@ -5,6 +5,13 @@ All notable changes to this project will be documented in this file.
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
6
6
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
+ ## [0.31.0](https://github.com/janeapp/riffer/compare/riffer/v0.30.0...riffer/v0.31.0) (2026-06-03)
9
+
10
+
11
+ ### Features
12
+
13
+ * forward session: through Agent.from_h/from_json for history seeding ([#295](https://github.com/janeapp/riffer/issues/295)) ([0b2eaa2](https://github.com/janeapp/riffer/commit/0b2eaa27f5154bfd61891d4e93b8938f7dce2ce6))
14
+
8
15
  ## [0.30.0](https://github.com/janeapp/riffer/compare/riffer/v0.29.1...riffer/v0.30.0) (2026-06-03)
9
16
 
10
17
 
@@ -1,28 +1,39 @@
1
1
  # Serialization
2
2
 
3
- `Riffer::Agent::Serializer` turns a **resolved agent** into a self-contained, provider-neutral data dict (`to_h`) and reconstructs a **runnable agent** from that dict (`from_h`). Use it to persist agent definitions outside of code, or to transfer them across a process/service boundary.
3
+ `Riffer::Agent::Serializer` turns a **resolved agent** into a self-contained, provider-neutral data hash (`to_h`) and reconstructs a **runnable agent** from that hash (`from_h`). Use it to persist agent definitions outside of code, or to transfer them across a process/service boundary.
4
4
 
5
5
  You normally reach it through the delegators on `Riffer::Agent`:
6
6
 
7
7
  ```ruby
8
- dict = agent.to_h # snapshot
9
- rebuilt = Riffer::Agent.from_h(dict) # reconstruct
8
+ data = agent.to_h # snapshot
9
+ rebuilt = Riffer::Agent.from_h(data) # reconstruct
10
10
  ```
11
11
 
12
- The dict is plain data — symbol-keyed, JSON-safe. For the wire, use the JSON helpers, which handle generating and parsing for you:
12
+ The hash is plain data — symbol-keyed, JSON-safe. For the wire, use the JSON helpers, which handle generating and parsing for you:
13
13
 
14
14
  ```ruby
15
15
  json = agent.to_json # or Riffer::Agent::Serializer.to_json(agent:)
16
16
  rebuilt = Riffer::Agent.from_json(json)
17
17
  ```
18
18
 
19
- The hash forms (`to_h` / `from_h`) are public too, if you want to embed the dict in a larger payload. `from_h` expects symbol keys, so parse with `JSON.parse(str, symbolize_names: true)` — or just use `from_json`, which does that for you.
19
+ The hash forms (`to_h` / `from_h`) are public too, if you want to embed the hash in a larger payload. `from_h` expects symbol keys, so parse with `JSON.parse(str, symbolize_names: true)` — or just use `from_json`, which does that for you.
20
20
 
21
21
  ### Runtime context
22
22
 
23
23
  `from_h` / `from_json` accept an optional `context:` — the rebuilt agent's **runtime** context, exactly the value you'd pass to `Agent.new(context:)`. It is **not** used to re-resolve the serialized definition (that's already resolved); it's threaded into tool dispatch and read by tools/runtimes at call time. Pass it when a tool or a remote runtime needs per-call data — e.g. `context: { tenant: "acme" }` for multi-tenant dispatch, or Maestro passing `context: { agent: self }` so its runtime can call back. Omit it (defaults to empty) when nothing downstream reads context.
24
24
 
25
- ## What the dict carries
25
+ ### Seeding conversation history
26
+
27
+ The hash carries the agent **definition**, not its conversation history (see [What does not transfer](#what-does-not-transfer)). To resume a persisted conversation, pass a `session:` — exactly the value you'd pass to `Agent.new(session:)`:
28
+
29
+ ```ruby
30
+ rebuilt = Riffer::Agent.from_h(data, session: persisted_session)
31
+ rebuilt.generate # continues from the seeded history
32
+ ```
33
+
34
+ The session is used **as-is**: the rebuilt agent does not prepend anything to it, so the caller owns its full contents — **including the system instruction message**. Omit `session:` and the rebuilt agent builds a fresh session, seeded with the hash's `instructions` and an empty history. Because a supplied session is authoritative, the hash's `instructions` are not re-injected into it — make sure your persisted session already contains the system message.
35
+
36
+ ## What the hash carries
26
37
 
27
38
  ```ruby
28
39
  {
@@ -41,7 +52,7 @@ The hash forms (`to_h` / `from_h`) are public too, if you want to embed the dict
41
52
 
42
53
  ### Resolved snapshot
43
54
 
44
- `to_h` reads the agent's **resolved** state, not its raw configuration. By the time you call it, `Agent.new` has already evaluated any `Proc`-based `model`, `instructions`, or `uses_tools` against the agent's own context — so the dict carries plain strings and data, never Procs. The receiver's `context:` drives runtime behavior (tool dispatch); it does **not** re-evaluate baked-in fields.
55
+ `to_h` reads the agent's **resolved** state, not its raw configuration. By the time you call it, `Agent.new` has already evaluated any `Proc`-based `model`, `instructions`, or `uses_tools` against the agent's own context — so the hash carries plain strings and data, never Procs. The receiver's `context:` drives runtime behavior (tool dispatch); it does **not** re-evaluate baked-in fields.
45
56
 
46
57
  ### Structured output
47
58
 
@@ -56,7 +67,7 @@ Tools cross as `{name, description, parameters_schema, timeout}` descriptors —
56
67
  When the rebuilt agent runs in the **same** codebase that defined the tools (e.g. persisting an agent definition and rehydrating it later), resolve each descriptor back to its real class:
57
68
 
58
69
  ```ruby
59
- rebuilt = Riffer::Agent.from_h(dict,
70
+ rebuilt = Riffer::Agent.from_h(data,
60
71
  tool_resolver: ->(descriptor) { MyToolRegistry.fetch(descriptor[:name]) })
61
72
  ```
62
73
 
@@ -67,7 +78,7 @@ The real classes carry their `#call` bodies, so the agent runs on the default `I
67
78
  When the receiver holds **only the Riffer gem**, the default `tool_resolver` synthesizes body-less **tool shells**. A shell advertises the tool's schema to the LLM but has no `#call` — invoking it in-process raises. Pair the default resolver with a remote `Riffer::Tools::Runtime` that forwards each call back to the origin:
68
79
 
69
80
  ```ruby
70
- rebuilt = Riffer::Agent.from_h(dict,
81
+ rebuilt = Riffer::Agent.from_h(data,
71
82
  tool_runtime: MyRemoteToolRuntime.new(client: rpc_client))
72
83
  ```
73
84
 
@@ -77,12 +88,12 @@ You own what a resolved tool does: a resolver may return real in-process classes
77
88
 
78
89
  ## `max_steps`
79
90
 
80
- Unlimited steps are `nil` at the agent level — set it with `max_steps nil`. On the wire, the serializer encodes that as **`-1`** (and decodes `-1` back to `nil`), so the dict stays portable across transports where JSON `null` is awkward — proto3, for one, can't distinguish `null` from an absent field. The `-1` is purely a wire detail: the DSL and your code only ever see `nil`, and the encode/decode handles the translation at the boundary.
91
+ Unlimited steps are `nil` at the agent level — set it with `max_steps nil`. On the wire, the serializer encodes that as **`-1`** (and decodes `-1` back to `nil`), so the hash stays portable across transports where JSON `null` is awkward — proto3, for one, can't distinguish `null` from an absent field. The `-1` is purely a wire detail: the DSL and your code only ever see `nil`, and the encode/decode handles the translation at the boundary.
81
92
 
82
93
  - **DSL** — integer = bounded, `nil` = unlimited, omitted = `Config`'s default (16).
83
94
  - **Wire** — integer = bounded, `-1` = unlimited, omitted = default (16).
84
95
 
85
- A finite integer round-trips as-is; a dict missing the key falls back to the default rather than running unbounded.
96
+ A finite integer round-trips as-is; a hash missing the key falls back to the default rather than running unbounded.
86
97
 
87
98
  ## Versioning
88
99
 
@@ -90,7 +101,7 @@ A finite integer round-trips as-is; a dict missing the key falls back to the def
90
101
 
91
102
  ## Secrets
92
103
 
93
- `provider_options` and `model_options` **ride on the wire as plain data** — they are part of the dict and _will_ transfer. Prefer configuring API keys via environment/global provider configuration rather than `provider_options`. **Never serialize an agent whose options carry sensitive values** — and if a serialized definition ever does, handle it as a secret (encrypt it, keep it out of logs).
104
+ `provider_options` and `model_options` **ride on the wire as plain data** — they are part of the hash and _will_ transfer. Prefer configuring API keys via environment/global provider configuration rather than `provider_options`. **Never serialize an agent whose options carry sensitive values** — and if a serialized definition ever does, handle it as a secret (encrypt it, keep it out of logs).
94
105
 
95
106
  ## What does **not** transfer
96
107
 
@@ -4,12 +4,12 @@
4
4
  require "json"
5
5
 
6
6
  # Riffer::Agent::Serializer turns a resolved agent into a self-contained,
7
- # provider-neutral data dict and back into a runnable agent. A pure module
7
+ # provider-neutral data hash and back into a runnable agent. A pure module
8
8
  # (sibling to Riffer::Agent::Run), reached most often through the
9
9
  # +Riffer::Agent#to_h+ / +Riffer::Agent.from_h+ delegators.
10
10
  #
11
- # The dict carries only data — no Procs, no class references, no tool
12
- # runtime. The same dict serves two rehydration targets:
11
+ # The hash carries only data — no Procs, no class references, no tool
12
+ # runtime. The same hash serves two rehydration targets:
13
13
  #
14
14
  # - <b>In-process</b> (a monolith persisting agent definitions): pass a
15
15
  # +tool_resolver+ that looks tool descriptors up in a local registry and
@@ -18,8 +18,8 @@ require "json"
18
18
  # resolver synthesizes body-less tool shells; inject a remote
19
19
  # +Riffer::Tools::Runtime+ to forward each call back to the origin.
20
20
  #
21
- # dict = Riffer::Agent::Serializer.to_h(agent: agent)
22
- # rebuilt = Riffer::Agent::Serializer.from_h(dict, context: {tenant: "acme"})
21
+ # data = Riffer::Agent::Serializer.to_h(agent: agent)
22
+ # rebuilt = Riffer::Agent::Serializer.from_h(data, context: {tenant: "acme"})
23
23
  #
24
24
  # == What does not transfer
25
25
  #
@@ -32,21 +32,21 @@ module Riffer::Agent::Serializer
32
32
  extend self
33
33
 
34
34
  # The wire format version. Bumped only on an incompatible change to the
35
- # dict shape; +from_h+ refuses any other version. See +from_h+ for the
35
+ # hash shape; +from_h+ refuses any other version. See +from_h+ for the
36
36
  # dispatch seam that carries back-compat decoders.
37
37
  SCHEMA_VERSION = 1 #: Integer
38
38
 
39
- # Raised by +from_h+ when the dict's +schema_version+ is unsupported.
39
+ # Raised by +from_h+ when the hash's +schema_version+ is unsupported.
40
40
  class VersionError < Riffer::ArgumentError; end
41
41
 
42
42
  # The default +tool_resolver+: synthesizes a body-less tool shell from a
43
43
  # descriptor. Its +#call+ raises — route shells through a remote runtime.
44
44
  DEFAULT_TOOL_RESOLVER = ->(descriptor) { build_tool_shell(descriptor) } #: ^(Hash[Symbol, untyped]) -> singleton(Riffer::Tool)
45
45
 
46
- # Snapshots a resolved agent into a self-contained wire dict.
46
+ # Snapshots a resolved agent into a self-contained wire hash.
47
47
  #
48
48
  # Reads the agent's resolved instance state — Proc-based settings have
49
- # already been evaluated against the agent's own context, so the dict
49
+ # already been evaluated against the agent's own context, so the hash
50
50
  # carries plain strings/data, never Procs. Tools are emitted as
51
51
  # +{name, description, parameters_schema, timeout}+ descriptors (the
52
52
  # resolved +agent.tools+, including MCP tools and +skill_activate+).
@@ -71,14 +71,20 @@ module Riffer::Agent::Serializer
71
71
  }
72
72
  end
73
73
 
74
- # Reconstructs a runnable agent from a wire dict.
74
+ # Reconstructs a runnable agent from a wire hash.
75
75
  #
76
- # [hash] a Symbol-keyed wire dict (parse JSON with +symbolize_names: true+).
76
+ # [hash] a Symbol-keyed wire hash (parse JSON with +symbolize_names: true+).
77
77
  # [context] the rebuilt agent's runtime context — the same value you'd pass
78
78
  # to +Agent.new(context:)+. It is *not* used to re-resolve serialized
79
- # config (the dict is already resolved); it is threaded into tool dispatch
79
+ # config (the hash is already resolved); it is threaded into tool dispatch
80
80
  # and read by tools/runtimes at call time (e.g. a remote runtime keying off
81
81
  # <tt>context[:tenant]</tt>). Defaults to an empty context.
82
+ # [session] an optional Riffer::Agent::Session to seed conversation history,
83
+ # forwarded verbatim to +Agent.new(session:)+. The hash carries the agent
84
+ # *definition*, not its history (see "What does not transfer"); pass a
85
+ # session here to resume a persisted conversation. Used as-is — the caller
86
+ # owns its contents, including the system instruction message. When omitted,
87
+ # a fresh session seeded with the hash's instructions is built.
82
88
  # [tool_resolver] maps a tool descriptor to a Riffer::Tool class. Defaults
83
89
  # to DEFAULT_TOOL_RESOLVER (body-less shells). Pass a registry lookup to
84
90
  # rebuild real, in-process tools.
@@ -87,16 +93,16 @@ module Riffer::Agent::Serializer
87
93
  # default (+Riffer.config.tool_runtime+).
88
94
  #
89
95
  # Raises Riffer::Agent::Serializer::VersionError on an unsupported
90
- # +schema_version+, and Riffer::ArgumentError on a malformed dict.
96
+ # +schema_version+, and Riffer::ArgumentError on a malformed hash.
91
97
  #
92
98
  #--
93
- #: (Hash[Symbol, untyped], ?context: Hash[Symbol, untyped]?, ?tool_resolver: ^(Hash[Symbol, untyped]) -> singleton(Riffer::Tool), ?tool_runtime: (singleton(Riffer::Tools::Runtime) | Riffer::Tools::Runtime | Proc)?) -> Riffer::Agent
94
- def from_h(hash, context: nil, tool_resolver: DEFAULT_TOOL_RESOLVER, tool_runtime: nil)
99
+ #: (Hash[Symbol, untyped], ?context: Hash[Symbol, untyped]?, ?session: Riffer::Agent::Session?, ?tool_resolver: ^(Hash[Symbol, untyped]) -> singleton(Riffer::Tool), ?tool_runtime: (singleton(Riffer::Tools::Runtime) | Riffer::Tools::Runtime | Proc)?) -> Riffer::Agent
100
+ def from_h(hash, context: nil, session: nil, tool_resolver: DEFAULT_TOOL_RESOLVER, tool_runtime: nil)
95
101
  # Version -> decoder dispatch. Adding a +when 2+ arm (a backwards-compatible
96
102
  # decoder) is how a future breaking change keeps older dicts readable.
97
103
  case hash[:schema_version]
98
104
  when SCHEMA_VERSION
99
- decode_v1(hash, context: context, tool_resolver: tool_resolver, tool_runtime: tool_runtime)
105
+ decode_v1(hash, context: context, session: session, tool_resolver: tool_resolver, tool_runtime: tool_runtime)
100
106
  else
101
107
  raise VersionError, "Unsupported schema_version: #{hash[:schema_version].inspect} (this Riffer supports #{SCHEMA_VERSION})"
102
108
  end
@@ -116,16 +122,16 @@ module Riffer::Agent::Serializer
116
122
  # +from_h+ for the arguments.
117
123
  #
118
124
  #--
119
- #: (String, ?context: Hash[Symbol, untyped]?, ?tool_resolver: ^(Hash[Symbol, untyped]) -> singleton(Riffer::Tool), ?tool_runtime: (singleton(Riffer::Tools::Runtime) | Riffer::Tools::Runtime | Proc)?) -> Riffer::Agent
120
- def from_json(json, context: nil, tool_resolver: DEFAULT_TOOL_RESOLVER, tool_runtime: nil)
121
- from_h(JSON.parse(json, symbolize_names: true), context: context, tool_resolver: tool_resolver, tool_runtime: tool_runtime)
125
+ #: (String, ?context: Hash[Symbol, untyped]?, ?session: Riffer::Agent::Session?, ?tool_resolver: ^(Hash[Symbol, untyped]) -> singleton(Riffer::Tool), ?tool_runtime: (singleton(Riffer::Tools::Runtime) | Riffer::Tools::Runtime | Proc)?) -> Riffer::Agent
126
+ def from_json(json, context: nil, session: nil, tool_resolver: DEFAULT_TOOL_RESOLVER, tool_runtime: nil)
127
+ from_h(JSON.parse(json, symbolize_names: true), context: context, session: session, tool_resolver: tool_resolver, tool_runtime: tool_runtime)
122
128
  end
123
129
 
124
130
  private
125
131
 
126
132
  #--
127
- #: (Hash[Symbol, untyped], context: Hash[Symbol, untyped]?, tool_resolver: ^(Hash[Symbol, untyped]) -> singleton(Riffer::Tool), tool_runtime: (singleton(Riffer::Tools::Runtime) | Riffer::Tools::Runtime | Proc)?) -> Riffer::Agent
128
- def decode_v1(hash, context:, tool_resolver:, tool_runtime:)
133
+ #: (Hash[Symbol, untyped], context: Hash[Symbol, untyped]?, session: Riffer::Agent::Session?, tool_resolver: ^(Hash[Symbol, untyped]) -> singleton(Riffer::Tool), tool_runtime: (singleton(Riffer::Tools::Runtime) | Riffer::Tools::Runtime | Proc)?) -> Riffer::Agent
134
+ def decode_v1(hash, context:, session:, tool_resolver:, tool_runtime:)
129
135
  tools = Array(hash[:tools]).map { |descriptor| tool_resolver.call(descriptor) }
130
136
 
131
137
  config_args = {
@@ -142,7 +148,11 @@ module Riffer::Agent::Serializer
142
148
  # Config default (Riffer.config.tool_runtime) applies.
143
149
  config_args[:tool_runtime] = tool_runtime if tool_runtime
144
150
 
145
- Riffer::Agent.new(config: Riffer::Agent::Config.new(**config_args), context: context)
151
+ # +session+ is forwarded verbatim: when nil, Agent.new seeds a fresh session
152
+ # from the decoded instructions; when supplied, Agent.new uses it as-is to
153
+ # resume persisted history. The hash never carries history (see "What does
154
+ # not transfer"), so this is the only seam for rehydrating a conversation.
155
+ Riffer::Agent.new(config: Riffer::Agent::Config.new(**config_args), context: context, session: session)
146
156
  end
147
157
 
148
158
  #--
@@ -153,7 +163,7 @@ module Riffer::Agent::Serializer
153
163
  end
154
164
 
155
165
  # The DSL represents unlimited steps as +nil+, but the wire encodes it as
156
- # +-1+ so the dict stays portable across transports where JSON +null+ is
166
+ # +-1+ so the hash stays portable across transports where JSON +null+ is
157
167
  # awkward (e.g. proto3, which can't tell null from an absent field). The
158
168
  # magic value lives only on the wire — +encode_max_steps+/+decode_max_steps+
159
169
  # translate at the boundary so neither the DSL nor consumers see it.
@@ -164,7 +174,7 @@ module Riffer::Agent::Serializer
164
174
  end
165
175
 
166
176
  # Reverses +encode_max_steps+: +-1+ (or a literal +null+) means unlimited.
167
- # An absent key falls back to the default — a partial dict must not silently
177
+ # An absent key falls back to the default — a partial hash must not silently
168
178
  # become an unbounded loop.
169
179
  #--
170
180
  #: (Hash[Symbol, untyped]) -> Numeric?
data/lib/riffer/agent.rb CHANGED
@@ -212,28 +212,28 @@ class Riffer::Agent
212
212
  new(context: context).stream(prompt, files: files)
213
213
  end
214
214
 
215
- # Reconstructs a runnable agent from a wire dict produced by +#to_h+.
215
+ # Reconstructs a runnable agent from a wire hash produced by +#to_h+.
216
216
  #
217
- # Delegates to Riffer::Agent::Serializer.from_h. See it for the
218
- # +tool_resolver+ / +tool_runtime+ injection points and what does not
219
- # transfer.
217
+ # Delegates to Riffer::Agent::Serializer.from_h. See it for the +session+
218
+ # seed, the +tool_resolver+ / +tool_runtime+ injection points, and what does
219
+ # not transfer.
220
220
  #
221
221
  #--
222
- #: (Hash[Symbol, untyped], ?context: Hash[Symbol, untyped]?, ?tool_resolver: ^(Hash[Symbol, untyped]) -> singleton(Riffer::Tool), ?tool_runtime: (singleton(Riffer::Tools::Runtime) | Riffer::Tools::Runtime | Proc)?) -> Riffer::Agent
223
- def self.from_h(hash, context: nil, tool_resolver: Riffer::Agent::Serializer::DEFAULT_TOOL_RESOLVER, tool_runtime: nil)
224
- Riffer::Agent::Serializer.from_h(hash, context: context, tool_resolver: tool_resolver, tool_runtime: tool_runtime)
222
+ #: (Hash[Symbol, untyped], ?context: Hash[Symbol, untyped]?, ?session: Riffer::Agent::Session?, ?tool_resolver: ^(Hash[Symbol, untyped]) -> singleton(Riffer::Tool), ?tool_runtime: (singleton(Riffer::Tools::Runtime) | Riffer::Tools::Runtime | Proc)?) -> Riffer::Agent
223
+ def self.from_h(hash, context: nil, session: nil, tool_resolver: Riffer::Agent::Serializer::DEFAULT_TOOL_RESOLVER, tool_runtime: nil)
224
+ Riffer::Agent::Serializer.from_h(hash, context: context, session: session, tool_resolver: tool_resolver, tool_runtime: tool_runtime)
225
225
  end
226
226
 
227
227
  # Reconstructs a runnable agent from a JSON string produced by +#to_json+.
228
228
  #
229
229
  # Delegates to Riffer::Agent::Serializer.from_json, which parses the JSON
230
230
  # (with symbol keys) for you. See Riffer::Agent::Serializer.from_h for the
231
- # +tool_resolver+ / +tool_runtime+ injection points.
231
+ # +session+ seed and the +tool_resolver+ / +tool_runtime+ injection points.
232
232
  #
233
233
  #--
234
- #: (String, ?context: Hash[Symbol, untyped]?, ?tool_resolver: ^(Hash[Symbol, untyped]) -> singleton(Riffer::Tool), ?tool_runtime: (singleton(Riffer::Tools::Runtime) | Riffer::Tools::Runtime | Proc)?) -> Riffer::Agent
235
- def self.from_json(json, context: nil, tool_resolver: Riffer::Agent::Serializer::DEFAULT_TOOL_RESOLVER, tool_runtime: nil)
236
- Riffer::Agent::Serializer.from_json(json, context: context, tool_resolver: tool_resolver, tool_runtime: tool_runtime)
234
+ #: (String, ?context: Hash[Symbol, untyped]?, ?session: Riffer::Agent::Session?, ?tool_resolver: ^(Hash[Symbol, untyped]) -> singleton(Riffer::Tool), ?tool_runtime: (singleton(Riffer::Tools::Runtime) | Riffer::Tools::Runtime | Proc)?) -> Riffer::Agent
235
+ def self.from_json(json, context: nil, session: nil, tool_resolver: Riffer::Agent::Serializer::DEFAULT_TOOL_RESOLVER, tool_runtime: nil)
236
+ Riffer::Agent::Serializer.from_json(json, context: context, session: session, tool_resolver: tool_resolver, tool_runtime: tool_runtime)
237
237
  end
238
238
 
239
239
  # Registers a guardrail for input, output, or both phases.
@@ -412,7 +412,7 @@ class Riffer::Agent
412
412
  end
413
413
 
414
414
  # Snapshots this resolved agent into a self-contained, provider-neutral
415
- # wire dict. Delegates to Riffer::Agent::Serializer.to_h.
415
+ # wire hash. Delegates to Riffer::Agent::Serializer.to_h.
416
416
  #
417
417
  #--
418
418
  #: () -> Hash[Symbol, untyped]
@@ -2,5 +2,5 @@
2
2
  # rbs_inline: enabled
3
3
 
4
4
  module Riffer
5
- VERSION = "0.30.0" #: String
5
+ VERSION = "0.31.0" #: String
6
6
  end
@@ -1,12 +1,12 @@
1
1
  # Generated from lib/riffer/agent/serializer.rb with RBS::Inline
2
2
 
3
3
  # Riffer::Agent::Serializer turns a resolved agent into a self-contained,
4
- # provider-neutral data dict and back into a runnable agent. A pure module
4
+ # provider-neutral data hash and back into a runnable agent. A pure module
5
5
  # (sibling to Riffer::Agent::Run), reached most often through the
6
6
  # +Riffer::Agent#to_h+ / +Riffer::Agent.from_h+ delegators.
7
7
  #
8
- # The dict carries only data — no Procs, no class references, no tool
9
- # runtime. The same dict serves two rehydration targets:
8
+ # The hash carries only data — no Procs, no class references, no tool
9
+ # runtime. The same hash serves two rehydration targets:
10
10
  #
11
11
  # - <b>In-process</b> (a monolith persisting agent definitions): pass a
12
12
  # +tool_resolver+ that looks tool descriptors up in a local registry and
@@ -15,8 +15,8 @@
15
15
  # resolver synthesizes body-less tool shells; inject a remote
16
16
  # +Riffer::Tools::Runtime+ to forward each call back to the origin.
17
17
  #
18
- # dict = Riffer::Agent::Serializer.to_h(agent: agent)
19
- # rebuilt = Riffer::Agent::Serializer.from_h(dict, context: {tenant: "acme"})
18
+ # data = Riffer::Agent::Serializer.to_h(agent: agent)
19
+ # rebuilt = Riffer::Agent::Serializer.from_h(data, context: {tenant: "acme"})
20
20
  #
21
21
  # == What does not transfer
22
22
  #
@@ -27,11 +27,11 @@
27
27
  # both ride on the wire as plain data.
28
28
  module Riffer::Agent::Serializer
29
29
  # The wire format version. Bumped only on an incompatible change to the
30
- # dict shape; +from_h+ refuses any other version. See +from_h+ for the
30
+ # hash shape; +from_h+ refuses any other version. See +from_h+ for the
31
31
  # dispatch seam that carries back-compat decoders.
32
32
  SCHEMA_VERSION: Integer
33
33
 
34
- # Raised by +from_h+ when the dict's +schema_version+ is unsupported.
34
+ # Raised by +from_h+ when the hash's +schema_version+ is unsupported.
35
35
  class VersionError < Riffer::ArgumentError
36
36
  end
37
37
 
@@ -39,10 +39,10 @@ module Riffer::Agent::Serializer
39
39
  # descriptor. Its +#call+ raises — route shells through a remote runtime.
40
40
  DEFAULT_TOOL_RESOLVER: ^(Hash[Symbol, untyped]) -> singleton(Riffer::Tool)
41
41
 
42
- # Snapshots a resolved agent into a self-contained wire dict.
42
+ # Snapshots a resolved agent into a self-contained wire hash.
43
43
  #
44
44
  # Reads the agent's resolved instance state — Proc-based settings have
45
- # already been evaluated against the agent's own context, so the dict
45
+ # already been evaluated against the agent's own context, so the hash
46
46
  # carries plain strings/data, never Procs. Tools are emitted as
47
47
  # +{name, description, parameters_schema, timeout}+ descriptors (the
48
48
  # resolved +agent.tools+, including MCP tools and +skill_activate+).
@@ -53,14 +53,20 @@ module Riffer::Agent::Serializer
53
53
  # : (agent: Riffer::Agent) -> Hash[Symbol, untyped]
54
54
  def to_h: (agent: Riffer::Agent) -> Hash[Symbol, untyped]
55
55
 
56
- # Reconstructs a runnable agent from a wire dict.
56
+ # Reconstructs a runnable agent from a wire hash.
57
57
  #
58
- # [hash] a Symbol-keyed wire dict (parse JSON with +symbolize_names: true+).
58
+ # [hash] a Symbol-keyed wire hash (parse JSON with +symbolize_names: true+).
59
59
  # [context] the rebuilt agent's runtime context — the same value you'd pass
60
60
  # to +Agent.new(context:)+. It is *not* used to re-resolve serialized
61
- # config (the dict is already resolved); it is threaded into tool dispatch
61
+ # config (the hash is already resolved); it is threaded into tool dispatch
62
62
  # and read by tools/runtimes at call time (e.g. a remote runtime keying off
63
63
  # <tt>context[:tenant]</tt>). Defaults to an empty context.
64
+ # [session] an optional Riffer::Agent::Session to seed conversation history,
65
+ # forwarded verbatim to +Agent.new(session:)+. The hash carries the agent
66
+ # *definition*, not its history (see "What does not transfer"); pass a
67
+ # session here to resume a persisted conversation. Used as-is — the caller
68
+ # owns its contents, including the system instruction message. When omitted,
69
+ # a fresh session seeded with the hash's instructions is built.
64
70
  # [tool_resolver] maps a tool descriptor to a Riffer::Tool class. Defaults
65
71
  # to DEFAULT_TOOL_RESOLVER (body-less shells). Pass a registry lookup to
66
72
  # rebuild real, in-process tools.
@@ -69,11 +75,11 @@ module Riffer::Agent::Serializer
69
75
  # default (+Riffer.config.tool_runtime+).
70
76
  #
71
77
  # Raises Riffer::Agent::Serializer::VersionError on an unsupported
72
- # +schema_version+, and Riffer::ArgumentError on a malformed dict.
78
+ # +schema_version+, and Riffer::ArgumentError on a malformed hash.
73
79
  #
74
80
  # --
75
- # : (Hash[Symbol, untyped], ?context: Hash[Symbol, untyped]?, ?tool_resolver: ^(Hash[Symbol, untyped]) -> singleton(Riffer::Tool), ?tool_runtime: (singleton(Riffer::Tools::Runtime) | Riffer::Tools::Runtime | Proc)?) -> Riffer::Agent
76
- def from_h: (Hash[Symbol, untyped], ?context: Hash[Symbol, untyped]?, ?tool_resolver: ^(Hash[Symbol, untyped]) -> singleton(Riffer::Tool), ?tool_runtime: (singleton(Riffer::Tools::Runtime) | Riffer::Tools::Runtime | Proc)?) -> Riffer::Agent
81
+ # : (Hash[Symbol, untyped], ?context: Hash[Symbol, untyped]?, ?session: Riffer::Agent::Session?, ?tool_resolver: ^(Hash[Symbol, untyped]) -> singleton(Riffer::Tool), ?tool_runtime: (singleton(Riffer::Tools::Runtime) | Riffer::Tools::Runtime | Proc)?) -> Riffer::Agent
82
+ def from_h: (Hash[Symbol, untyped], ?context: Hash[Symbol, untyped]?, ?session: Riffer::Agent::Session?, ?tool_resolver: ^(Hash[Symbol, untyped]) -> singleton(Riffer::Tool), ?tool_runtime: (singleton(Riffer::Tools::Runtime) | Riffer::Tools::Runtime | Proc)?) -> Riffer::Agent
77
83
 
78
84
  # Snapshots a resolved agent to a JSON string. Convenience over
79
85
  # <tt>JSON.generate(to_h(agent:))</tt>.
@@ -87,21 +93,21 @@ module Riffer::Agent::Serializer
87
93
  # +from_h+ for the arguments.
88
94
  #
89
95
  # --
90
- # : (String, ?context: Hash[Symbol, untyped]?, ?tool_resolver: ^(Hash[Symbol, untyped]) -> singleton(Riffer::Tool), ?tool_runtime: (singleton(Riffer::Tools::Runtime) | Riffer::Tools::Runtime | Proc)?) -> Riffer::Agent
91
- def from_json: (String, ?context: Hash[Symbol, untyped]?, ?tool_resolver: ^(Hash[Symbol, untyped]) -> singleton(Riffer::Tool), ?tool_runtime: (singleton(Riffer::Tools::Runtime) | Riffer::Tools::Runtime | Proc)?) -> Riffer::Agent
96
+ # : (String, ?context: Hash[Symbol, untyped]?, ?session: Riffer::Agent::Session?, ?tool_resolver: ^(Hash[Symbol, untyped]) -> singleton(Riffer::Tool), ?tool_runtime: (singleton(Riffer::Tools::Runtime) | Riffer::Tools::Runtime | Proc)?) -> Riffer::Agent
97
+ def from_json: (String, ?context: Hash[Symbol, untyped]?, ?session: Riffer::Agent::Session?, ?tool_resolver: ^(Hash[Symbol, untyped]) -> singleton(Riffer::Tool), ?tool_runtime: (singleton(Riffer::Tools::Runtime) | Riffer::Tools::Runtime | Proc)?) -> Riffer::Agent
92
98
 
93
99
  private
94
100
 
95
101
  # --
96
- # : (Hash[Symbol, untyped], context: Hash[Symbol, untyped]?, tool_resolver: ^(Hash[Symbol, untyped]) -> singleton(Riffer::Tool), tool_runtime: (singleton(Riffer::Tools::Runtime) | Riffer::Tools::Runtime | Proc)?) -> Riffer::Agent
97
- def decode_v1: (Hash[Symbol, untyped], context: Hash[Symbol, untyped]?, tool_resolver: ^(Hash[Symbol, untyped]) -> singleton(Riffer::Tool), tool_runtime: (singleton(Riffer::Tools::Runtime) | Riffer::Tools::Runtime | Proc)?) -> Riffer::Agent
102
+ # : (Hash[Symbol, untyped], context: Hash[Symbol, untyped]?, session: Riffer::Agent::Session?, tool_resolver: ^(Hash[Symbol, untyped]) -> singleton(Riffer::Tool), tool_runtime: (singleton(Riffer::Tools::Runtime) | Riffer::Tools::Runtime | Proc)?) -> Riffer::Agent
103
+ def decode_v1: (Hash[Symbol, untyped], context: Hash[Symbol, untyped]?, session: Riffer::Agent::Session?, tool_resolver: ^(Hash[Symbol, untyped]) -> singleton(Riffer::Tool), tool_runtime: (singleton(Riffer::Tools::Runtime) | Riffer::Tools::Runtime | Proc)?) -> Riffer::Agent
98
104
 
99
105
  # --
100
106
  # : (Hash[Symbol, untyped]?) -> Riffer::Params?
101
107
  def decode_structured_output: (Hash[Symbol, untyped]?) -> Riffer::Params?
102
108
 
103
109
  # The DSL represents unlimited steps as +nil+, but the wire encodes it as
104
- # +-1+ so the dict stays portable across transports where JSON +null+ is
110
+ # +-1+ so the hash stays portable across transports where JSON +null+ is
105
111
  # awkward (e.g. proto3, which can't tell null from an absent field). The
106
112
  # magic value lives only on the wire — +encode_max_steps+/+decode_max_steps+
107
113
  # translate at the boundary so neither the DSL nor consumers see it.
@@ -110,7 +116,7 @@ module Riffer::Agent::Serializer
110
116
  def encode_max_steps: (Numeric?) -> Numeric
111
117
 
112
118
  # Reverses +encode_max_steps+: +-1+ (or a literal +null+) means unlimited.
113
- # An absent key falls back to the default — a partial dict must not silently
119
+ # An absent key falls back to the default — a partial hash must not silently
114
120
  # become an unbounded loop.
115
121
  # --
116
122
  # : (Hash[Symbol, untyped]) -> Numeric?
@@ -164,25 +164,25 @@ class Riffer::Agent
164
164
  # : (?String?, ?files: Array[Hash[Symbol, untyped] | Riffer::Messages::FilePart]?, ?context: Hash[Symbol, untyped]?) -> Enumerator[Riffer::StreamEvents::Base, void]
165
165
  def self.stream: (?String?, ?files: Array[Hash[Symbol, untyped] | Riffer::Messages::FilePart]?, ?context: Hash[Symbol, untyped]?) -> Enumerator[Riffer::StreamEvents::Base, void]
166
166
 
167
- # Reconstructs a runnable agent from a wire dict produced by +#to_h+.
167
+ # Reconstructs a runnable agent from a wire hash produced by +#to_h+.
168
168
  #
169
- # Delegates to Riffer::Agent::Serializer.from_h. See it for the
170
- # +tool_resolver+ / +tool_runtime+ injection points and what does not
171
- # transfer.
169
+ # Delegates to Riffer::Agent::Serializer.from_h. See it for the +session+
170
+ # seed, the +tool_resolver+ / +tool_runtime+ injection points, and what does
171
+ # not transfer.
172
172
  #
173
173
  # --
174
- # : (Hash[Symbol, untyped], ?context: Hash[Symbol, untyped]?, ?tool_resolver: ^(Hash[Symbol, untyped]) -> singleton(Riffer::Tool), ?tool_runtime: (singleton(Riffer::Tools::Runtime) | Riffer::Tools::Runtime | Proc)?) -> Riffer::Agent
175
- def self.from_h: (Hash[Symbol, untyped], ?context: Hash[Symbol, untyped]?, ?tool_resolver: ^(Hash[Symbol, untyped]) -> singleton(Riffer::Tool), ?tool_runtime: (singleton(Riffer::Tools::Runtime) | Riffer::Tools::Runtime | Proc)?) -> Riffer::Agent
174
+ # : (Hash[Symbol, untyped], ?context: Hash[Symbol, untyped]?, ?session: Riffer::Agent::Session?, ?tool_resolver: ^(Hash[Symbol, untyped]) -> singleton(Riffer::Tool), ?tool_runtime: (singleton(Riffer::Tools::Runtime) | Riffer::Tools::Runtime | Proc)?) -> Riffer::Agent
175
+ def self.from_h: (Hash[Symbol, untyped], ?context: Hash[Symbol, untyped]?, ?session: Riffer::Agent::Session?, ?tool_resolver: ^(Hash[Symbol, untyped]) -> singleton(Riffer::Tool), ?tool_runtime: (singleton(Riffer::Tools::Runtime) | Riffer::Tools::Runtime | Proc)?) -> Riffer::Agent
176
176
 
177
177
  # Reconstructs a runnable agent from a JSON string produced by +#to_json+.
178
178
  #
179
179
  # Delegates to Riffer::Agent::Serializer.from_json, which parses the JSON
180
180
  # (with symbol keys) for you. See Riffer::Agent::Serializer.from_h for the
181
- # +tool_resolver+ / +tool_runtime+ injection points.
181
+ # +session+ seed and the +tool_resolver+ / +tool_runtime+ injection points.
182
182
  #
183
183
  # --
184
- # : (String, ?context: Hash[Symbol, untyped]?, ?tool_resolver: ^(Hash[Symbol, untyped]) -> singleton(Riffer::Tool), ?tool_runtime: (singleton(Riffer::Tools::Runtime) | Riffer::Tools::Runtime | Proc)?) -> Riffer::Agent
185
- def self.from_json: (String, ?context: Hash[Symbol, untyped]?, ?tool_resolver: ^(Hash[Symbol, untyped]) -> singleton(Riffer::Tool), ?tool_runtime: (singleton(Riffer::Tools::Runtime) | Riffer::Tools::Runtime | Proc)?) -> Riffer::Agent
184
+ # : (String, ?context: Hash[Symbol, untyped]?, ?session: Riffer::Agent::Session?, ?tool_resolver: ^(Hash[Symbol, untyped]) -> singleton(Riffer::Tool), ?tool_runtime: (singleton(Riffer::Tools::Runtime) | Riffer::Tools::Runtime | Proc)?) -> Riffer::Agent
185
+ def self.from_json: (String, ?context: Hash[Symbol, untyped]?, ?session: Riffer::Agent::Session?, ?tool_resolver: ^(Hash[Symbol, untyped]) -> singleton(Riffer::Tool), ?tool_runtime: (singleton(Riffer::Tools::Runtime) | Riffer::Tools::Runtime | Proc)?) -> Riffer::Agent
186
186
 
187
187
  # Registers a guardrail for input, output, or both phases.
188
188
  #
@@ -331,7 +331,7 @@ class Riffer::Agent
331
331
  def interrupt!: (?(String | Symbol)?) -> void
332
332
 
333
333
  # Snapshots this resolved agent into a self-contained, provider-neutral
334
- # wire dict. Delegates to Riffer::Agent::Serializer.to_h.
334
+ # wire hash. Delegates to Riffer::Agent::Serializer.to_h.
335
335
  #
336
336
  # --
337
337
  # : () -> Hash[Symbol, untyped]
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: riffer
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.30.0
4
+ version: 0.31.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jake Bottrall