phronomy 0.20.0 → 0.22.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.
@@ -3,15 +3,18 @@
3
3
  module Phronomy
4
4
  module VectorStore
5
5
  module Embeddings
6
- # Abstract interface for embedding adapters.
6
+ # Public extension SPI for embedding adapters.
7
7
  #
8
- # Concrete implementations must override {#embed} and return a vector
9
- # as an +Array<Float>+.
8
+ # Concrete implementations override {#embed}. Phronomy owns the async
9
+ # bridge: {#embed_async} routes the synchronous implementation through the
10
+ # bounded OffloadPool and returns a {Phronomy::Task}.
11
+ #
12
+ # @api public
10
13
  class Base
11
14
  # Embed the given text and return a vector representation.
12
15
  #
13
- # @param text [String] the text to embed
14
- # @param cancellation_token [Phronomy::Concurrency::CancellationToken, nil] optional; raises CancellationError when cancelled
16
+ # @param text [String] the text to embed
17
+ # @param cancellation_token [Phronomy::Concurrency::CancellationToken, nil]
15
18
  # @return [Array<Float>] the embedding vector
16
19
  # @api public
17
20
  def embed(text, cancellation_token = nil)
@@ -19,13 +22,12 @@ module Phronomy
19
22
  raise NotImplementedError, "#{self.class}#embed is not implemented"
20
23
  end
21
24
 
22
- # Submits an {#embed} call to {OffloadPool} and returns an
23
- # {OffloadPool::PendingOperation}.
25
+ # Submits an {#embed} call to {OffloadPool}.
24
26
  #
25
27
  # @param text [String]
26
28
  # @param cancellation_token [Phronomy::Concurrency::CancellationToken, nil]
27
- # @param timeout [Numeric, nil] seconds before the operation is abandoned
28
- # @return [OffloadPool::PendingOperation]
29
+ # @param timeout [Numeric, nil] operation-wide submit timeout
30
+ # @return [Phronomy::Task]
29
31
  # @api public
30
32
  def embed_async(text, cancellation_token = nil, timeout: nil)
31
33
  Phronomy::Runtime.instance.offload.submit(
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Phronomy
4
- VERSION = "0.20.0"
4
+ VERSION = "0.22.0"
5
5
  end
@@ -13,7 +13,8 @@
13
13
  # Target: mutation score >= 80% for each listed subject.
14
14
  # Baseline scores (as of initial run):
15
15
  # Phronomy::WorkflowContext 84.85%
16
- # Phronomy::Tool::Base 55.74%
16
+ # Phronomy::Agent::Context::Capability::Base 55.74%
17
+ # (public facade alias: Phronomy::Tool::Base; same Class object)
17
18
  #
18
19
  # Note: mutation testing is slow (~1-5 min per subject). Run locally or via
19
20
  # the nightly-mutation GitHub Actions workflow.
@@ -0,0 +1,36 @@
1
+ module Phronomy
2
+ module Agent
3
+ class Base
4
+ def self.model: (?String name) -> String?
5
+ def self.instructions: (?untyped text) { (untyped) -> untyped } -> untyped
6
+ | (?untyped text) -> untyped
7
+ def self.tools: (?Hash[Class, String?] definitions) -> untyped
8
+ def self.tool_aliases: () -> Hash[Class, String]
9
+ def self.provider: (?Symbol name) -> Symbol?
10
+ def self.temperature: (?Float value) -> Float?
11
+ def self.max_iterations: (?Integer value) -> Integer
12
+ def self.cache_instructions: (?bool enabled) -> bool?
13
+ def self.max_output_tokens: (?Integer value) -> Integer?
14
+ def self.context_window: (?Integer value) -> Integer?
15
+ def self.agent_definition: (?id: String?, ?version: Integer?) -> Hash[Symbol, untyped]
16
+ def self.create: (?agent_id: String, ?context: untyped, ?knowledge: Array[untyped], ?persistence: Persistence?, ?metadata: Hash[untyped, untyped]) -> instance
17
+ def self.load: (String agent_id, persistence: Persistence) -> instance
18
+ def self.live_for_execution: (String execution_id) -> instance
19
+
20
+ attr_reader agent_id: String
21
+ attr_reader persistence: Persistence
22
+
23
+ def invoke: (untyped input, **untyped) -> Hash[Symbol, untyped]
24
+ def invoke_async: (untyped input, **untyped) -> Task[Hash[Symbol, untyped]]
25
+ def stream: (untyped input, **untyped) { (untyped event) -> void } -> Hash[Symbol, untyped]
26
+ def stream_async: (untyped input, **untyped) -> Task[Hash[Symbol, untyped]]
27
+ def transcript: () -> Array[untyped]
28
+ def add_knowledge: (untyped content, ?metadata: Hash[untyped, untyped]) -> self
29
+ def clear_transcript!: () -> untyped
30
+ def clear_knowledge!: () -> untyped
31
+ def reset_context!: () -> untyped
32
+ def close!: () -> untyped
33
+ def purge!: () -> bool
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,50 @@
1
+ module Phronomy
2
+ module Filter
3
+ class Base
4
+ def call: (untyped value, **untyped context) -> untyped
5
+ end
6
+ end
7
+
8
+ module OutputParser
9
+ class Base
10
+ def parse: (String text) -> untyped
11
+ def invoke: (untyped input, ?config: Hash[untyped, untyped]) -> untyped
12
+ end
13
+ end
14
+
15
+ module Tracing
16
+ class Base
17
+ def start_span: (String name, **untyped attributes) -> untyped
18
+ def finish_span: (untyped span, ?output: untyped, ?usage: untyped, ?error: Exception?) -> untyped
19
+ def trace: [A] (String name, ?input: untyped, **untyped meta) { (untyped span) -> [A, untyped] } -> A
20
+ end
21
+ end
22
+
23
+ module VectorStore
24
+ class Base
25
+ def add: (id: String, embedding: Array[Float], ?metadata: Hash[untyped, untyped], ?cancellation_token: Concurrency::CancellationToken?) -> untyped
26
+ def search: (query_embedding: Array[Float], ?k: Integer, ?cancellation_token: Concurrency::CancellationToken?) -> Array[Hash[Symbol, untyped]]
27
+ def remove: (id: String) -> untyped
28
+ def clear: () -> untyped
29
+ def size: () -> Integer
30
+
31
+ def add_async: (id: String, embedding: Array[Float], ?metadata: Hash[untyped, untyped], ?cancellation_token: Concurrency::CancellationToken?, ?timeout: Numeric?) -> Task[untyped]
32
+ def search_async: (query_embedding: Array[Float], ?k: Integer, ?cancellation_token: Concurrency::CancellationToken?, ?timeout: Numeric?) -> Task[Array[Hash[Symbol, untyped]]]
33
+ def remove_async: (id: String, ?cancellation_token: Concurrency::CancellationToken?, ?timeout: Numeric?) -> Task[untyped]
34
+ def clear_async: (?cancellation_token: Concurrency::CancellationToken?, ?timeout: Numeric?) -> Task[untyped]
35
+ end
36
+
37
+ module Embeddings
38
+ class Base
39
+ def embed: (String text, ?Concurrency::CancellationToken? cancellation_token) -> Array[Float]
40
+ def embed_async: (String text, ?Concurrency::CancellationToken? cancellation_token, ?timeout: Numeric?) -> Task[Array[Float]]
41
+ end
42
+ end
43
+ end
44
+ module Testing
45
+ module PersistenceContract
46
+ SHARED_EXAMPLES: Array[String]
47
+ end
48
+ end
49
+
50
+ end
@@ -0,0 +1,11 @@
1
+ module Phronomy
2
+ module LLMAdapter
3
+ class Base
4
+ def complete: (untyped chat, String? message, ?config: Hash[untyped, untyped]) -> untyped
5
+ def stream: (untyped chat, String? message, ?config: Hash[untyped, untyped]) { (untyped chunk) -> void } -> untyped
6
+ end
7
+
8
+ class RubyLLM < Base
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,70 @@
1
+ module Phronomy
2
+ interface _ContentRepository
3
+ def put: (String bytes, canonicalization_version: untyped) -> String
4
+ def fetch: (String content_id) -> String
5
+ def exist?: (String content_id) -> bool
6
+ end
7
+
8
+ interface _AgentRepository
9
+ def create: (untyped root) -> untyped
10
+ def load: (String agent_id) -> untyped
11
+ def save: (String agent_id, expected_revision: Integer, root: untyped) -> untyped
12
+ def delete: (String agent_id) -> untyped
13
+ end
14
+
15
+ interface _JournalRepository
16
+ def append: (String agent_id, expected_position: Integer, records: Array[untyped]) -> Array[untyped]
17
+ def read: (String agent_id, ?after: Integer?, ?limit: Integer?) -> Array[untyped]
18
+ def head: (String agent_id) -> Integer
19
+ def delete: (String agent_id) -> untyped
20
+ end
21
+
22
+ interface _ExecutionRepository
23
+ def create_active: (untyped execution) -> untyped
24
+ def load: (String execution_id) -> untyped
25
+ def save: (String execution_id, expected_revision: Integer, execution: untyped) -> untyped
26
+ def list_active: (String agent_id) -> Array[untyped]
27
+ def delete: (String execution_id) -> untyped
28
+ def delete_for_agent: (String agent_id) -> untyped
29
+ def assert_idle!: (String agent_id) -> untyped
30
+ end
31
+
32
+ interface _WorkflowStateRepository
33
+ def load: (String thread_id) -> Hash[untyped, untyped]?
34
+ def save: (String thread_id, expected_revision: Integer?, snapshot: Hash[untyped, untyped]) -> untyped
35
+ def delete: (String thread_id, expected_revision: Integer) -> untyped
36
+ end
37
+
38
+ interface _PersistenceTransactionView
39
+ def contents: () -> _ContentRepository
40
+ def agents: () -> _AgentRepository
41
+ def journals: () -> _JournalRepository
42
+ def executions: () -> _ExecutionRepository
43
+ def workflow_states: () -> _WorkflowStateRepository
44
+ def assert_agent_watermark!: (agent_id: String, agent_revision: Integer, journal_position: Integer) -> true
45
+ end
46
+
47
+ class Persistence
48
+ class ConflictError < Phronomy::Error
49
+ end
50
+ class NotFoundError < Phronomy::Error
51
+ end
52
+ class UnsupportedBackendError < Phronomy::Error
53
+ end
54
+ class SerializationError < Phronomy::Error
55
+ end
56
+
57
+ REQUIRED_CAPABILITIES: Hash[Symbol, bool]
58
+
59
+ attr_reader contents: _ContentRepository
60
+ attr_reader agents: _AgentRepository
61
+ attr_reader journals: _JournalRepository
62
+ attr_reader executions: _ExecutionRepository
63
+ attr_reader workflow_states: _WorkflowStateRepository
64
+
65
+ def initialize: (contents: _ContentRepository, agents: _AgentRepository, journals: _JournalRepository, executions: _ExecutionRepository, workflow_states: _WorkflowStateRepository) -> void
66
+ def capabilities: () -> Hash[Symbol, bool]
67
+ def transaction: () { (_PersistenceTransactionView) -> untyped } -> untyped
68
+ def assert_agent_watermark!: (agent_id: String, agent_revision: Integer, journal_position: Integer) -> true
69
+ end
70
+ end
@@ -0,0 +1,43 @@
1
+ module Phronomy
2
+ class Task[A]
3
+ attr_reader name: String?
4
+ attr_reader parent: Task[untyped]?
5
+
6
+ def status: () -> (:pending | :completed | :failed | :cancelled)
7
+ def done?: () -> bool
8
+ def alive?: () -> bool
9
+ def wait_result: (?timeout: Numeric?) -> A
10
+ def on_complete: () { (A?, Exception?) -> void } -> self
11
+ def map: [B] () { (A) -> B } -> Task[B]
12
+ end
13
+
14
+ module Concurrency
15
+ class CancellationToken
16
+ def self.timeout_after: (Numeric seconds) -> CancellationToken
17
+ def initialize: (?monotonic_deadline: Float?) -> void
18
+ def remaining_monotonic_seconds: () -> Float?
19
+ def on_cancel: () { () -> void } -> self
20
+ def cancel!: () -> self
21
+ def cancelled?: () -> bool
22
+ def raise_if_cancelled!: (?String message) -> nil
23
+ end
24
+ end
25
+ end
26
+
27
+ module Phronomy
28
+ class InvocationContext
29
+ attr_reader thread_id: String?
30
+ attr_reader session_id: String?
31
+ attr_reader user_id: String?
32
+ attr_reader cancellation_token: Concurrency::CancellationToken?
33
+ attr_reader deadline: untyped
34
+ attr_reader tracer_span: untyped
35
+ attr_reader token_budget: Integer?
36
+ attr_reader approval_policy: untyped
37
+ attr_reader redaction_policy: untyped
38
+ attr_reader task_id: String?
39
+ attr_reader parent_task_id: String?
40
+
41
+ def initialize: (?thread_id: String?, ?session_id: String?, ?user_id: String?, ?cancellation_token: Concurrency::CancellationToken?, ?deadline: untyped, ?tracer_span: untyped, ?token_budget: Integer?, ?approval_policy: untyped, ?redaction_policy: untyped, ?task_id: String?, ?parent_task_id: String?) -> void
42
+ end
43
+ end
@@ -0,0 +1,39 @@
1
+ module Phronomy
2
+ module Tool
3
+ class Base
4
+ def self.tool_name: (?untyped value) -> String?
5
+ def self.description: (?String text) -> String?
6
+ def self.desc: (?String text) -> String?
7
+ def self.parameters: () -> Hash[untyped, untyped]
8
+ def self.params_schema_definition: () -> untyped
9
+ def self.provider_params: () -> Hash[untyped, untyped]
10
+ def self.param: (untyped name, ?enum: untyped, ?properties: untyped, **untyped options) -> untyped
11
+ def self.execution_mode: (?(:cooperative | :offloaded) value) -> (:cooperative | :offloaded)
12
+ def self.on_error: (?(:raise | :suppress) behavior) -> (:raise | :suppress)
13
+ def self.on_schema_error: (?untyped behavior) -> untyped
14
+ def self.requires_approval: (?untyped value) { () -> untyped } -> untyped
15
+ | (?untyped value) -> untyped
16
+ def self.approval_facts: () { (untyped, untyped) -> untyped } -> untyped
17
+ | () -> untyped
18
+ def self.redact_params: (*untyped names) -> Array[Symbol]
19
+ def self.max_result_size: (?untyped value) -> untyped
20
+
21
+ def name: () -> String
22
+ def params_schema: () -> untyped
23
+ def call: (Hash[untyped, untyped] args, ?cancellation_token: Concurrency::CancellationToken?) -> untyped
24
+ def call_async: (Hash[untyped, untyped] args, ?cancellation_token: Concurrency::CancellationToken?, ?config: Hash[untyped, untyped]) -> Task[untyped]
25
+ def requires_approval?: () -> untyped
26
+ def tool_origin: () -> Symbol
27
+ def approval_metadata: () -> Hash[untyped, untyped]
28
+ def execute: (**untyped args) -> untyped
29
+ end
30
+ end
31
+
32
+ module Agent
33
+ module Context
34
+ module Capability
35
+ class Base = ::Phronomy::Tool::Base
36
+ end
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,30 @@
1
+ module Phronomy
2
+ module WorkflowContext
3
+ def phase: () -> Symbol
4
+ def halted?: () -> bool
5
+ def thread_id: () -> String?
6
+ def merge: (Hash[Symbol, untyped] updates) -> self
7
+ def to_h: () -> Hash[Symbol, untyped]
8
+ end
9
+
10
+ class Workflow
11
+ def self.define: (Class context_class, ?persistence: Persistence?) { () -> void } -> Workflow
12
+
13
+ def invoke: (untyped input, ?config: Hash[untyped, untyped], ?invocation_context: untyped) -> untyped
14
+ def invoke_async: (untyped input, ?config: Hash[untyped, untyped], ?invocation_context: untyped) -> Task[untyped]
15
+ def stream: (untyped input, ?config: Hash[untyped, untyped], ?invocation_context: untyped) { (untyped) -> void } -> untyped
16
+ def resume: (state: untyped, ?input: untyped) -> untyped
17
+ def send_event: (state: untyped, event: Symbol, ?input: untyped) -> untyped
18
+ def signal: (thread_id: String, event: Symbol, ?payload: untyped) -> bool
19
+
20
+ class Builder
21
+ def initial: (Symbol state_name) -> untyped
22
+ def state: (Symbol name, ?action: untyped) -> untyped
23
+ def entry: (Symbol name, untyped callable) -> untyped
24
+ def exit: (Symbol name, untyped callable) -> untyped
25
+ def wait_state: (Symbol name) -> untyped
26
+ def transition: (from: Symbol, to: Symbol, ?guard: untyped, ?on: Symbol?, ?action: untyped) -> untyped
27
+ def build: () -> Workflow
28
+ end
29
+ end
30
+ end
data/sig/phronomy.rbs CHANGED
@@ -1,4 +1,52 @@
1
1
  module Phronomy
2
2
  VERSION: String
3
- # See the writing guide of rbs: https://github.com/ruby/rbs#guides
3
+
4
+ class Error < StandardError
5
+ end
6
+
7
+ class ParseError < Error
8
+ end
9
+
10
+ class RecursionLimitError < Error
11
+ end
12
+
13
+ class ToolError < Error
14
+ end
15
+
16
+ class TimeoutError < Error
17
+ end
18
+
19
+ class ConfigurationError < Error
20
+ end
21
+
22
+ class TransportError < Error
23
+ end
24
+
25
+ class RateLimitError < TransportError
26
+ end
27
+
28
+ class AuthenticationError < TransportError
29
+ end
30
+
31
+ class ContextLengthError < Error
32
+ end
33
+
34
+ class CancellationError < Error
35
+ end
36
+
37
+ class EventLoopReentrancyError < Error
38
+ end
39
+
40
+ class PoolShutdownError < Error
41
+ end
42
+
43
+ class BackpressureError < Error
44
+ end
45
+
46
+ class Configuration
47
+ attr_accessor llm_adapter: LLMAdapter::Base
48
+ end
49
+
50
+ def self.configuration: () -> Configuration
51
+ def self.configure: () { (Configuration) -> void } -> void
4
52
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: phronomy
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.20.0
4
+ version: 0.22.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Raizo T.C.S
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2026-08-16 00:00:00.000000000 Z
11
+ date: 2026-08-17 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: ruby_llm
@@ -120,6 +120,7 @@ files:
120
120
  - docs/decisions/012-canonical-execution-log-and-context-policy.md
121
121
  - docs/decisions/013-journal-backed-knowledge-as-context-candidates.md
122
122
  - docs/decisions/014-unified-persistence-durable-state.md
123
+ - docs/decisions/015-tool-public-facade-and-rbs-boundary.md
123
124
  - docs/features.md
124
125
  - docs/getting-started.md
125
126
  - docs/mcp-client.md
@@ -259,6 +260,8 @@ files:
259
260
  - lib/phronomy/testing/persistence_contract/an_agent_repository.rb
260
261
  - lib/phronomy/testing/persistence_contract/an_execution_repository.rb
261
262
  - lib/phronomy/token_usage.rb
263
+ - lib/phronomy/tool.rb
264
+ - lib/phronomy/tool/base.rb
262
265
  - lib/phronomy/tools/agent.rb
263
266
  - lib/phronomy/tools/mcp.rb
264
267
  - lib/phronomy/tools/vector_search.rb
@@ -298,6 +301,13 @@ files:
298
301
  - scripts/migrate_spec_inline_pass3.rb
299
302
  - scripts/run_mutation.sh
300
303
  - sig/phronomy.rbs
304
+ - sig/phronomy/agent.rbs
305
+ - sig/phronomy/extensions.rbs
306
+ - sig/phronomy/llm_adapter.rbs
307
+ - sig/phronomy/persistence.rbs
308
+ - sig/phronomy/runtime.rbs
309
+ - sig/phronomy/tool.rbs
310
+ - sig/phronomy/workflow.rbs
301
311
  homepage: https://github.com/Raizo-TCS/phronomy
302
312
  licenses:
303
313
  - MIT