phronomy 0.33.0 → 0.34.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 +62 -0
- data/CONTRIBUTING.md +5 -5
- data/README.md +15 -2
- data/RELEASE_CHECKLIST.md +3 -1
- data/SECURITY.md +1 -1
- data/VERIFY.sh +1 -9
- data/docs/architecture/error-api-review.md +6 -2
- data/docs/architecture/structured-output-and-scope.md +3 -4
- data/docs/decisions/016-semantic-multi-agent-handoff.md +6 -5
- data/docs/features.md +2 -2
- data/docs/migrations/cleanup-audit.md +89 -0
- data/docs/migrations/legacy-format-removal.md +55 -0
- data/docs/migrations/r8-unit6.md +4 -2
- data/docs/migrations/vector-async-clients.md +6 -1
- data/docs/shared-state-sharing.md +14 -4
- data/lib/phronomy/agent/execution/agent_invocation.rb +0 -4
- data/lib/phronomy/agent/handoff/transfer_projection.rb +0 -9
- data/lib/phronomy/agent/journal/journal_record.rb +2 -2
- data/lib/phronomy/agent/recovery/recovery_rules.rb +0 -12
- data/lib/phronomy/agent/store.rb +3 -8
- data/lib/phronomy/agent/tool_execution/tool_invocation.rb +0 -3
- data/lib/phronomy/context/context_policy.rb +3 -7
- data/lib/phronomy/context/llm_input_manifest.rb +3 -3
- data/lib/phronomy/engine/runtime/shutdown_result.rb +0 -5
- data/lib/phronomy/execution/execution.rb +0 -11
- data/lib/phronomy/multi_agent/shared_state.rb +5 -8
- data/lib/phronomy/version.rb +1 -1
- metadata +4 -11
- data/lib/phronomy/execution/scheduler_reentrancy_error.rb +0 -9
- data/lib/phronomy/persistence/migration/initial_format_migration.rb +0 -226
- data/scripts/add_to_h_to_token_doubles.rb +0 -33
- data/scripts/add_to_h_unnamed_doubles.rb +0 -27
- data/scripts/migrate_generator_verifier_result.rb +0 -18
- data/scripts/migrate_spec_agent_definition.rb +0 -108
- data/scripts/migrate_spec_agent_definition_pass2.rb +0 -53
- data/scripts/migrate_spec_inline_pass3.rb +0 -24
- data/sig/phronomy/persistence/migration.rbs +0 -13
|
@@ -35,7 +35,7 @@ module Phronomy
|
|
|
35
35
|
role: role,
|
|
36
36
|
content: content,
|
|
37
37
|
content_format: content_format || inferred_content_format(content),
|
|
38
|
-
estimated_tokens:
|
|
38
|
+
estimated_tokens: estimate_tokens(content),
|
|
39
39
|
required: required,
|
|
40
40
|
provenance: generated_provenance,
|
|
41
41
|
metadata: metadata
|
|
@@ -56,7 +56,7 @@ module Phronomy
|
|
|
56
56
|
role: role,
|
|
57
57
|
content: content,
|
|
58
58
|
content_format: content_format || inferred_content_format(content),
|
|
59
|
-
estimated_tokens:
|
|
59
|
+
estimated_tokens: estimate_tokens(content),
|
|
60
60
|
required: required,
|
|
61
61
|
provenance: generated_provenance,
|
|
62
62
|
metadata: metadata
|
|
@@ -82,7 +82,7 @@ module Phronomy
|
|
|
82
82
|
content: content,
|
|
83
83
|
content_format: content_format || inferred_content_format(content),
|
|
84
84
|
sequence: sequence,
|
|
85
|
-
estimated_tokens:
|
|
85
|
+
estimated_tokens: estimate_tokens(content),
|
|
86
86
|
required: required,
|
|
87
87
|
provenance: generated_provenance,
|
|
88
88
|
tool_call_id: tool_call_id,
|
|
@@ -121,10 +121,6 @@ module Phronomy
|
|
|
121
121
|
def inferred_content_format(content)
|
|
122
122
|
content.is_a?(String) ? :text : :json
|
|
123
123
|
end
|
|
124
|
-
|
|
125
|
-
def estimate_content(content)
|
|
126
|
-
estimate_tokens(content)
|
|
127
|
-
end
|
|
128
124
|
end
|
|
129
125
|
end
|
|
130
126
|
end
|
|
@@ -149,9 +149,9 @@ module Phronomy
|
|
|
149
149
|
freeze
|
|
150
150
|
end
|
|
151
151
|
|
|
152
|
-
# Current-format-only durable decoder. Historical
|
|
153
|
-
#
|
|
154
|
-
#
|
|
152
|
+
# Current-format-only durable decoder. Historical versions are rejected.
|
|
153
|
+
# Unlike the Ruby construction API, this decoder never coerces durable
|
|
154
|
+
# field types.
|
|
155
155
|
def self.from_h(hash)
|
|
156
156
|
source = strict_source!(
|
|
157
157
|
hash,
|
|
@@ -52,11 +52,6 @@ module Phronomy
|
|
|
52
52
|
%i[not_started terminated].include?(@event_loop_status) &&
|
|
53
53
|
@task_registry_status == :empty
|
|
54
54
|
end
|
|
55
|
-
|
|
56
|
-
# Runtime reset is safe when cleanup completed, even if execution failed.
|
|
57
|
-
def success?
|
|
58
|
-
cleanup_complete?
|
|
59
|
-
end
|
|
60
55
|
end
|
|
61
56
|
end
|
|
62
57
|
end
|
|
@@ -127,22 +127,11 @@ module Phronomy
|
|
|
127
127
|
observed
|
|
128
128
|
end
|
|
129
129
|
|
|
130
|
-
# Scope/admission hooks shared with framework-owned operations.
|
|
131
|
-
# @api private
|
|
132
|
-
def __while_open(&block)
|
|
133
|
-
@scope.__while_open(&block)
|
|
134
|
-
end
|
|
135
|
-
|
|
136
130
|
# @api private
|
|
137
131
|
def __open?
|
|
138
132
|
@scope.__open?
|
|
139
133
|
end
|
|
140
134
|
|
|
141
|
-
# @api private
|
|
142
|
-
def __cancellation_token
|
|
143
|
-
@token
|
|
144
|
-
end
|
|
145
|
-
|
|
146
135
|
# @api private
|
|
147
136
|
def __cancellation_error
|
|
148
137
|
@scope.__cancellation_error
|
|
@@ -323,14 +323,11 @@ module Phronomy
|
|
|
323
323
|
# application's definition-version namespace independent from framework
|
|
324
324
|
# instrumentation.
|
|
325
325
|
instrumented_def = instrumented_definition_for(researcher_class)
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
)
|
|
332
|
-
tools(definitions)
|
|
333
|
-
end
|
|
326
|
+
researcher_class.with_tools(
|
|
327
|
+
definitions,
|
|
328
|
+
id: instrumented_def.fetch(:id),
|
|
329
|
+
version: instrumented_def.fetch(:version)
|
|
330
|
+
)
|
|
334
331
|
end
|
|
335
332
|
|
|
336
333
|
def build_read_tool(store)
|
data/lib/phronomy/version.rb
CHANGED
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.
|
|
4
|
+
version: 0.34.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-11-
|
|
11
|
+
date: 2026-11-15 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: ruby_llm
|
|
@@ -345,8 +345,10 @@ files:
|
|
|
345
345
|
- docs/migrations/0.19.md
|
|
346
346
|
- docs/migrations/0.22.md
|
|
347
347
|
- docs/migrations/0.31.md
|
|
348
|
+
- docs/migrations/cleanup-audit.md
|
|
348
349
|
- docs/migrations/durable-semantic-coordination-v2.md
|
|
349
350
|
- docs/migrations/handoff-runner-multi-agent.md
|
|
351
|
+
- docs/migrations/legacy-format-removal.md
|
|
350
352
|
- docs/migrations/neutral-storage-spi.md
|
|
351
353
|
- docs/migrations/parallel-tool-chat-removal.md
|
|
352
354
|
- docs/migrations/persistence-failure-contracts.md
|
|
@@ -561,7 +563,6 @@ files:
|
|
|
561
563
|
- lib/phronomy/execution/runnable.rb
|
|
562
564
|
- lib/phronomy/execution/runtime_shutdown_error.rb
|
|
563
565
|
- lib/phronomy/execution/runtime_shutdown_reentrancy_error.rb
|
|
564
|
-
- lib/phronomy/execution/scheduler_reentrancy_error.rb
|
|
565
566
|
- lib/phronomy/execution/task_result.rb
|
|
566
567
|
- lib/phronomy/execution/timeout_error.rb
|
|
567
568
|
- lib/phronomy/execution/wait_policy.rb
|
|
@@ -636,7 +637,6 @@ files:
|
|
|
636
637
|
- lib/phronomy/output_parser/structured_parser.rb
|
|
637
638
|
- lib/phronomy/persistence/content_repository.rb
|
|
638
639
|
- lib/phronomy/persistence/errors.rb
|
|
639
|
-
- lib/phronomy/persistence/migration/initial_format_migration.rb
|
|
640
640
|
- lib/phronomy/persistence/persistence.rb
|
|
641
641
|
- lib/phronomy/persistence/save_outcome.rb
|
|
642
642
|
- lib/phronomy/persistence/snapshot_comparison.rb
|
|
@@ -749,16 +749,10 @@ files:
|
|
|
749
749
|
- lib/phronomy/workflow/runtime_binding/workflow_phase_machine_builder.rb
|
|
750
750
|
- lib/phronomy/workflow/runtime_binding/workflow_task_session.rb
|
|
751
751
|
- lib/phronomy/workflow/runtime_binding/workflow_terminal_policy.rb
|
|
752
|
-
- scripts/add_to_h_to_token_doubles.rb
|
|
753
|
-
- scripts/add_to_h_unnamed_doubles.rb
|
|
754
752
|
- scripts/api_snapshot.rb
|
|
755
753
|
- scripts/check_api_annotations.rb
|
|
756
754
|
- scripts/check_readme_ruby.rb
|
|
757
755
|
- scripts/check_readme_runnable.rb
|
|
758
|
-
- scripts/migrate_generator_verifier_result.rb
|
|
759
|
-
- scripts/migrate_spec_agent_definition.rb
|
|
760
|
-
- scripts/migrate_spec_agent_definition_pass2.rb
|
|
761
|
-
- scripts/migrate_spec_inline_pass3.rb
|
|
762
756
|
- scripts/run_mutation.sh
|
|
763
757
|
- scripts/storage_spi_snapshot.rb
|
|
764
758
|
- sig/_private/phronomy/agent/settings.rbs
|
|
@@ -820,7 +814,6 @@ files:
|
|
|
820
814
|
- sig/phronomy/multi_agent/team_not_found_error.rbs
|
|
821
815
|
- sig/phronomy/persistence.rbs
|
|
822
816
|
- sig/phronomy/persistence/errors.rbs
|
|
823
|
-
- sig/phronomy/persistence/migration.rbs
|
|
824
817
|
- sig/phronomy/persistence/transaction.rbs
|
|
825
818
|
- sig/phronomy/persistence_composition.rbs
|
|
826
819
|
- sig/phronomy/runtime_composition/configuration.rbs
|
|
@@ -1,9 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
require_relative "event_loop_reentrancy_error"
|
|
4
|
-
|
|
5
|
-
module Phronomy
|
|
6
|
-
# Backward-compatible error class name for callers that still rescue the old
|
|
7
|
-
# scheduler-oriented exception. New code should use EventLoopReentrancyError.
|
|
8
|
-
class SchedulerReentrancyError < EventLoopReentrancyError; end
|
|
9
|
-
end
|
|
@@ -1,226 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
module Phronomy
|
|
4
|
-
class Persistence
|
|
5
|
-
module Migration
|
|
6
|
-
# Explicit logical conversion from the pre-S3 unversioned representations
|
|
7
|
-
# present at the ACS-06 fixed base to the initial 0.1 durable formats.
|
|
8
|
-
#
|
|
9
|
-
# This module does not scan a database or write records. A backend-specific
|
|
10
|
-
# migration runner is responsible for physical enumeration, transactions,
|
|
11
|
-
# and replacement. Normal Runtime load never calls these methods.
|
|
12
|
-
#
|
|
13
|
-
# @api public
|
|
14
|
-
module InitialFormatMigration
|
|
15
|
-
PRE_S3_AGENT_ROOT_KEYS = Phronomy::Agent::AgentRoot::ATTRIBUTES.map(&:to_s).freeze
|
|
16
|
-
PRE_S3_AGENT_EXECUTION_KEYS = Phronomy::Agent::AgentExecution::ATTRIBUTES.map(&:to_s).freeze
|
|
17
|
-
PRE_S3_JOURNAL_KEYS = Phronomy::Agent::JournalRecord::ATTRIBUTES.map(&:to_s).freeze
|
|
18
|
-
PRE_S3_LLM_CALL_KEYS = Phronomy::Agent::LLMCallRecord::ATTRIBUTES.map(&:to_s).freeze
|
|
19
|
-
PRE_S3_APPROVAL_REQUIRED_KEYS = %w[id items created_at].freeze
|
|
20
|
-
PRE_S3_APPROVAL_PARENT_KEYS = %w[execution_id agent_invocation_id].freeze
|
|
21
|
-
PRE_S3_APPROVAL_OPTIONAL_KEYS = %w[approved].freeze
|
|
22
|
-
PRE_S3_APPROVAL_ITEM_KEYS = %w[
|
|
23
|
-
tool_invocation_id tool_call_id tool_name arguments facts reason origin metadata
|
|
24
|
-
].freeze
|
|
25
|
-
|
|
26
|
-
module_function
|
|
27
|
-
|
|
28
|
-
def agent_root(hash)
|
|
29
|
-
source = stringify_keys(hash)
|
|
30
|
-
validate_exact_keys!(
|
|
31
|
-
source,
|
|
32
|
-
PRE_S3_AGENT_ROOT_KEYS,
|
|
33
|
-
label: "pre-S3 AgentRoot"
|
|
34
|
-
)
|
|
35
|
-
Phronomy::Agent::Persistence::Codec.encode_agent_root(
|
|
36
|
-
Phronomy::Agent::AgentRoot.from_h(source)
|
|
37
|
-
)
|
|
38
|
-
rescue Phronomy::Storage::SerializationError
|
|
39
|
-
raise
|
|
40
|
-
rescue => error
|
|
41
|
-
migration_error("AgentRoot", error)
|
|
42
|
-
end
|
|
43
|
-
|
|
44
|
-
def agent_execution(hash)
|
|
45
|
-
source = stringify_keys(hash)
|
|
46
|
-
validate_exact_keys!(
|
|
47
|
-
source,
|
|
48
|
-
PRE_S3_AGENT_EXECUTION_KEYS,
|
|
49
|
-
label: "pre-S3 AgentExecution"
|
|
50
|
-
)
|
|
51
|
-
source["working_records"] = Array(source.fetch("working_records")).map.with_index do |record, index|
|
|
52
|
-
normalize_legacy_journal_record(
|
|
53
|
-
record,
|
|
54
|
-
label: "pre-S3 AgentExecution working_records[#{index}]"
|
|
55
|
-
)
|
|
56
|
-
end
|
|
57
|
-
source["llm_calls"] = Array(source.fetch("llm_calls")).map.with_index do |call, index|
|
|
58
|
-
normalized = stringify_keys(call)
|
|
59
|
-
validate_exact_keys!(
|
|
60
|
-
normalized,
|
|
61
|
-
PRE_S3_LLM_CALL_KEYS,
|
|
62
|
-
label: "pre-S3 AgentExecution llm_calls[#{index}]"
|
|
63
|
-
)
|
|
64
|
-
normalized
|
|
65
|
-
end
|
|
66
|
-
source["approval_request"] = normalize_legacy_approval_request(
|
|
67
|
-
source["approval_request"],
|
|
68
|
-
execution_id: source.fetch("execution_id")
|
|
69
|
-
)
|
|
70
|
-
execution = Phronomy::Agent::AgentExecution.from_h(source)
|
|
71
|
-
Phronomy::Agent::Persistence::Codec.encode_agent_execution(execution)
|
|
72
|
-
rescue Phronomy::Storage::SerializationError
|
|
73
|
-
raise
|
|
74
|
-
rescue => error
|
|
75
|
-
migration_error("AgentExecution", error)
|
|
76
|
-
end
|
|
77
|
-
|
|
78
|
-
def journal_record(hash)
|
|
79
|
-
source = normalize_legacy_journal_record(hash, label: "pre-S3 JournalRecord")
|
|
80
|
-
Phronomy::Agent::Persistence::Codec.encode_journal_record(
|
|
81
|
-
Phronomy::Agent::JournalRecord.from_h(source)
|
|
82
|
-
)
|
|
83
|
-
rescue Phronomy::Storage::SerializationError
|
|
84
|
-
raise
|
|
85
|
-
rescue => error
|
|
86
|
-
migration_error("JournalRecord", error)
|
|
87
|
-
end
|
|
88
|
-
|
|
89
|
-
def workflow_state(workflow_instance_id:, revision:, snapshot:)
|
|
90
|
-
Phronomy::Workflow::Persistence::Codec.encode_workflow_state(
|
|
91
|
-
workflow_instance_id: workflow_instance_id,
|
|
92
|
-
workflow_revision: Integer(revision),
|
|
93
|
-
snapshot: snapshot
|
|
94
|
-
)
|
|
95
|
-
rescue Phronomy::Storage::SerializationError
|
|
96
|
-
raise
|
|
97
|
-
rescue => error
|
|
98
|
-
migration_error("Workflow state", error)
|
|
99
|
-
end
|
|
100
|
-
|
|
101
|
-
# LLMInputManifest is a ContentStore codec boundary, not a
|
|
102
|
-
# Storage::DurableRecord. Its pre-CG-07 integer version 1 is migrated
|
|
103
|
-
# explicitly to the pre-1.0 string version "0.1".
|
|
104
|
-
def llm_input_manifest(hash)
|
|
105
|
-
source = stringify_keys(hash)
|
|
106
|
-
old_version = source.fetch("version")
|
|
107
|
-
unless old_version == 1 || old_version == "1"
|
|
108
|
-
raise Phronomy::Storage::SerializationError,
|
|
109
|
-
"unsupported pre-S3 LLMInputManifest version: #{old_version.inspect}"
|
|
110
|
-
end
|
|
111
|
-
source["version"] = Phronomy::Context::LLMInputManifest::VERSION
|
|
112
|
-
Phronomy::Context::LLMInputManifest.from_h(source).to_h
|
|
113
|
-
rescue Phronomy::Storage::SerializationError
|
|
114
|
-
raise
|
|
115
|
-
rescue => error
|
|
116
|
-
migration_error("LLMInputManifest", error)
|
|
117
|
-
end
|
|
118
|
-
|
|
119
|
-
def normalize_legacy_journal_record(hash, label:)
|
|
120
|
-
source = stringify_keys(hash)
|
|
121
|
-
validate_allowed_keys!(
|
|
122
|
-
source,
|
|
123
|
-
required_keys: PRE_S3_JOURNAL_KEYS,
|
|
124
|
-
optional_keys: ["correlation_id"],
|
|
125
|
-
label: label
|
|
126
|
-
)
|
|
127
|
-
source.delete("correlation_id")
|
|
128
|
-
source
|
|
129
|
-
end
|
|
130
|
-
private_class_method :normalize_legacy_journal_record
|
|
131
|
-
|
|
132
|
-
def normalize_legacy_approval_request(request, execution_id:)
|
|
133
|
-
return nil unless request
|
|
134
|
-
|
|
135
|
-
source = stringify_keys(request)
|
|
136
|
-
validate_allowed_keys!(
|
|
137
|
-
source,
|
|
138
|
-
required_keys: PRE_S3_APPROVAL_REQUIRED_KEYS,
|
|
139
|
-
optional_keys: PRE_S3_APPROVAL_PARENT_KEYS + PRE_S3_APPROVAL_OPTIONAL_KEYS,
|
|
140
|
-
label: "pre-S3 approval_request"
|
|
141
|
-
)
|
|
142
|
-
parents = PRE_S3_APPROVAL_PARENT_KEYS.select { |key| source.key?(key) }
|
|
143
|
-
unless parents.length == 1
|
|
144
|
-
raise Phronomy::Storage::SerializationError,
|
|
145
|
-
"pre-S3 approval_request must contain exactly one parent identity, got #{parents.inspect}"
|
|
146
|
-
end
|
|
147
|
-
|
|
148
|
-
items = source.fetch("items")
|
|
149
|
-
unless items.is_a?(Array) && !items.empty?
|
|
150
|
-
raise Phronomy::Storage::SerializationError,
|
|
151
|
-
"pre-S3 approval_request items must be a non-empty Array"
|
|
152
|
-
end
|
|
153
|
-
source["items"] = items.map.with_index do |item, index|
|
|
154
|
-
normalized = stringify_keys(item)
|
|
155
|
-
validate_exact_keys!(
|
|
156
|
-
normalized,
|
|
157
|
-
PRE_S3_APPROVAL_ITEM_KEYS,
|
|
158
|
-
label: "pre-S3 approval_request items[#{index}]"
|
|
159
|
-
)
|
|
160
|
-
normalized
|
|
161
|
-
end
|
|
162
|
-
|
|
163
|
-
if source.key?("agent_invocation_id")
|
|
164
|
-
source.delete("agent_invocation_id")
|
|
165
|
-
source["execution_id"] = execution_id.to_s
|
|
166
|
-
elsif source.fetch("execution_id").to_s != execution_id.to_s
|
|
167
|
-
raise Phronomy::Storage::SerializationError,
|
|
168
|
-
"pre-S3 approval_request execution_id does not match AgentExecution"
|
|
169
|
-
end
|
|
170
|
-
source
|
|
171
|
-
end
|
|
172
|
-
private_class_method :normalize_legacy_approval_request
|
|
173
|
-
|
|
174
|
-
def validate_allowed_keys!(hash, required_keys:, optional_keys:, label:)
|
|
175
|
-
actual = hash.keys.sort
|
|
176
|
-
missing = required_keys.sort - actual
|
|
177
|
-
unknown = actual - (required_keys + optional_keys).sort
|
|
178
|
-
return hash if missing.empty? && unknown.empty?
|
|
179
|
-
|
|
180
|
-
details = []
|
|
181
|
-
details << "missing=#{missing.inspect}" unless missing.empty?
|
|
182
|
-
details << "unknown=#{unknown.inspect}" unless unknown.empty?
|
|
183
|
-
raise Phronomy::Storage::SerializationError,
|
|
184
|
-
"#{label} schema mismatch (#{details.join(", ")})"
|
|
185
|
-
end
|
|
186
|
-
private_class_method :validate_allowed_keys!
|
|
187
|
-
|
|
188
|
-
def validate_exact_keys!(hash, expected_keys, label:)
|
|
189
|
-
validate_allowed_keys!(
|
|
190
|
-
hash,
|
|
191
|
-
required_keys: expected_keys,
|
|
192
|
-
optional_keys: [],
|
|
193
|
-
label: label
|
|
194
|
-
)
|
|
195
|
-
end
|
|
196
|
-
private_class_method :validate_exact_keys!
|
|
197
|
-
|
|
198
|
-
def stringify_keys(hash)
|
|
199
|
-
unless hash.is_a?(Hash)
|
|
200
|
-
raise Phronomy::Storage::SerializationError,
|
|
201
|
-
"migration input must be a Hash"
|
|
202
|
-
end
|
|
203
|
-
hash.each_with_object({}) do |(key, value), result|
|
|
204
|
-
unless key.is_a?(String) || key.is_a?(Symbol)
|
|
205
|
-
raise Phronomy::Storage::SerializationError,
|
|
206
|
-
"migration key must be String or Symbol, got #{key.class}"
|
|
207
|
-
end
|
|
208
|
-
string_key = key.to_s
|
|
209
|
-
if result.key?(string_key)
|
|
210
|
-
raise Phronomy::Storage::SerializationError,
|
|
211
|
-
"duplicate migration key after normalization: #{string_key.inspect}"
|
|
212
|
-
end
|
|
213
|
-
result[string_key] = value
|
|
214
|
-
end
|
|
215
|
-
end
|
|
216
|
-
private_class_method :stringify_keys
|
|
217
|
-
|
|
218
|
-
def migration_error(label, error)
|
|
219
|
-
raise Phronomy::Storage::SerializationError,
|
|
220
|
-
"cannot migrate pre-S3 #{label}: #{error.class}: #{error.message}"
|
|
221
|
-
end
|
|
222
|
-
private_class_method :migration_error
|
|
223
|
-
end
|
|
224
|
-
end
|
|
225
|
-
end
|
|
226
|
-
end
|
|
@@ -1,33 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env ruby
|
|
2
|
-
# frozen_string_literal: true
|
|
3
|
-
|
|
4
|
-
# Adds to_h to all Tokens-like doubles so ExecutionCoordinator can serialize usage.
|
|
5
|
-
|
|
6
|
-
files = Dir.glob("spec/**/*.rb").sort
|
|
7
|
-
|
|
8
|
-
total = 0
|
|
9
|
-
files.each do |f|
|
|
10
|
-
content = File.read(f)
|
|
11
|
-
modified = content.gsub(
|
|
12
|
-
/double\(("[^"]+"),\s*(input: \d+, output: \d+, cached: \d+, cache_creation: \d+)\)/
|
|
13
|
-
) do
|
|
14
|
-
name = $1
|
|
15
|
-
attrs = $2
|
|
16
|
-
next $& if $&.include?("to_h")
|
|
17
|
-
|
|
18
|
-
h_entries = attrs.split(",").map(&:strip).map do |pair|
|
|
19
|
-
k, v = pair.split(":").map(&:strip)
|
|
20
|
-
"\"#{k}\" => #{v}"
|
|
21
|
-
end.join(", ")
|
|
22
|
-
|
|
23
|
-
total += 1
|
|
24
|
-
"double(#{name}, #{attrs}, to_h: {#{h_entries}})"
|
|
25
|
-
end
|
|
26
|
-
|
|
27
|
-
if modified != content
|
|
28
|
-
File.write(f, modified)
|
|
29
|
-
puts " patched: #{f}"
|
|
30
|
-
end
|
|
31
|
-
end
|
|
32
|
-
|
|
33
|
-
puts "\nAdded to_h to #{total} Tokens doubles."
|
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env ruby
|
|
2
|
-
# frozen_string_literal: true
|
|
3
|
-
|
|
4
|
-
# Adds to_h to unnamed (no-string-name) Tokens doubles like double(input: 5, ...)
|
|
5
|
-
|
|
6
|
-
files = Dir.glob("spec/**/*.rb").sort
|
|
7
|
-
|
|
8
|
-
total = 0
|
|
9
|
-
files.each do |f|
|
|
10
|
-
content = File.read(f)
|
|
11
|
-
modified = content.gsub(
|
|
12
|
-
/double\((input: (\d+), output: (\d+), cached: (\d+), cache_creation: (\d+))\)/
|
|
13
|
-
) do
|
|
14
|
-
attrs, a, b, c, d = $1, $2, $3, $4, $5
|
|
15
|
-
next $& if $&.include?("to_h")
|
|
16
|
-
|
|
17
|
-
total += 1
|
|
18
|
-
"double(#{attrs}, to_h: {\"input\" => #{a}, \"output\" => #{b}, \"cached\" => #{c}, \"cache_creation\" => #{d}})"
|
|
19
|
-
end
|
|
20
|
-
|
|
21
|
-
if modified != content
|
|
22
|
-
File.write(f, modified)
|
|
23
|
-
puts " patched: #{f}"
|
|
24
|
-
end
|
|
25
|
-
end
|
|
26
|
-
|
|
27
|
-
puts "\nAdded to_h to #{total} unnamed Tokens doubles."
|
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env ruby
|
|
2
|
-
# frozen_string_literal: true
|
|
3
|
-
|
|
4
|
-
# Explicit one-time conversion of a pre-structured-output Result JSON object.
|
|
5
|
-
# Never infer old formats in the runtime reader; never overwrite the source.
|
|
6
|
-
require_relative "../lib/phronomy"
|
|
7
|
-
|
|
8
|
-
abort "Usage: ruby scripts/migrate_generator_verifier_result.rb OLD.json NEW.json" unless ARGV.length == 2
|
|
9
|
-
source, destination = ARGV
|
|
10
|
-
value = JSON.parse(File.read(source), allow_duplicate_key: false, allow_nan: false,
|
|
11
|
-
allow_comments: false, allow_invalid_escape: false, allow_control_characters: false)
|
|
12
|
-
abort "Expected an old Result object without failure" unless value.is_a?(Hash) && !value.key?("failure")
|
|
13
|
-
value["failure"] = nil
|
|
14
|
-
result = Phronomy::GeneratorVerifier::Result.from_h(value)
|
|
15
|
-
File.open(destination, File::WRONLY | File::CREAT | File::EXCL) do |file|
|
|
16
|
-
file.write(JSON.pretty_generate(result.to_h))
|
|
17
|
-
file.write("\n")
|
|
18
|
-
end
|
|
@@ -1,108 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env ruby
|
|
2
|
-
# frozen_string_literal: true
|
|
3
|
-
|
|
4
|
-
# Migrates spec files to add agent_definition to Agent::Base subclasses.
|
|
5
|
-
# - Named classes: inserts after the class declaration line
|
|
6
|
-
# - Anonymous blocks (do...end): inserts after the opening `do`
|
|
7
|
-
# - Inline anonymous ({ ... }): expands to multi-line with agent_definition
|
|
8
|
-
# Safe: skips classes that already have agent_definition
|
|
9
|
-
|
|
10
|
-
require "fileutils"
|
|
11
|
-
|
|
12
|
-
SPEC_DIR = File.expand_path("../spec", __dir__)
|
|
13
|
-
AGENT_BASE_PATTERN = /Phronomy::Agent::Base/
|
|
14
|
-
|
|
15
|
-
def snake_case(class_name)
|
|
16
|
-
# Remove trailing Agent/Tool suffixes, snake_case the name
|
|
17
|
-
name = class_name.split("::").last
|
|
18
|
-
name.gsub(/([A-Z]+)([A-Z][a-z])/, '\1_\2')
|
|
19
|
-
.gsub(/([a-z\d])([A-Z])/, '\1_\2')
|
|
20
|
-
.downcase
|
|
21
|
-
end
|
|
22
|
-
|
|
23
|
-
counter = 0
|
|
24
|
-
files_modified = 0
|
|
25
|
-
|
|
26
|
-
Dir.glob("#{SPEC_DIR}/**/*.rb").sort.each do |file|
|
|
27
|
-
lines = File.readlines(file, chomp: false)
|
|
28
|
-
new_lines = []
|
|
29
|
-
modified = false
|
|
30
|
-
i = 0
|
|
31
|
-
|
|
32
|
-
while i < lines.length
|
|
33
|
-
line = lines[i]
|
|
34
|
-
|
|
35
|
-
# ── Named class: class FooAgent < Phronomy::Agent::Base ──────────────────
|
|
36
|
-
if line =~ /^(\s*)class\s+(\w+)\s*<\s*(?:::)?Phronomy::Agent::Base\s*$/
|
|
37
|
-
indent = $1
|
|
38
|
-
class_name = $2
|
|
39
|
-
id = snake_case(class_name).sub(/_agent$/, "-agent").tr("_", "-")
|
|
40
|
-
id = "#{id}-agent" unless id.end_with?("-agent")
|
|
41
|
-
new_lines << line
|
|
42
|
-
i += 1
|
|
43
|
-
# Check if agent_definition already present in the class body
|
|
44
|
-
look_ahead = lines[i..]&.take(10) || []
|
|
45
|
-
unless look_ahead.any? { |l| l =~ /agent_definition/ }
|
|
46
|
-
new_lines << "#{indent} agent_definition id: #{id.inspect}, version: 1\n"
|
|
47
|
-
counter += 1
|
|
48
|
-
modified = true
|
|
49
|
-
end
|
|
50
|
-
next
|
|
51
|
-
end
|
|
52
|
-
|
|
53
|
-
# ── Anonymous block (do...end): Class.new(Phronomy::Agent::Base) do ─────
|
|
54
|
-
if line =~ /^(\s*)([\w.]*\s*=\s*)?Class\.new\((?:::)?Phronomy::Agent::Base\)\s+do\s*(\|\w+\|)?\s*$/
|
|
55
|
-
indent = $1
|
|
56
|
-
# Check if agent_definition already present
|
|
57
|
-
look_ahead = lines[i + 1..]&.take(10) || []
|
|
58
|
-
new_lines << line
|
|
59
|
-
i += 1
|
|
60
|
-
unless look_ahead.any? { |l| l =~ /agent_definition/ }
|
|
61
|
-
# Derive indentation from the `do` block body (first non-blank line)
|
|
62
|
-
inner = lines[i..]&.take(5)&.find { |l| l.strip.length > 0 } || ""
|
|
63
|
-
inner_indent = inner.match(/^(\s*)/)[1]
|
|
64
|
-
inner_indent = "#{indent} " if inner_indent.strip.length > 0 && inner_indent.length < indent.length + 2
|
|
65
|
-
new_lines << "#{inner_indent}agent_definition id: \"test-agent-#{counter += 1}\", version: 1\n"
|
|
66
|
-
modified = true
|
|
67
|
-
end
|
|
68
|
-
next
|
|
69
|
-
end
|
|
70
|
-
|
|
71
|
-
# ── Inline anonymous: Class.new(Phronomy::Agent::Base) { ... } ───────────
|
|
72
|
-
if line =~ /^(\s*)((?:[\w.]*\s*=\s*)?)Class\.new\((?:::)?Phronomy::Agent::Base\)\s*\{(.+)\}\s*$/
|
|
73
|
-
indent = $1
|
|
74
|
-
assignment = $2
|
|
75
|
-
body = $3
|
|
76
|
-
# Skip if agent_definition already present
|
|
77
|
-
if /agent_definition/.match?(body)
|
|
78
|
-
new_lines << line
|
|
79
|
-
else
|
|
80
|
-
id = counter += 1
|
|
81
|
-
definition_line = "agent_definition id: \"test-agent-#{id}\", version: 1;"
|
|
82
|
-
new_lines << "#{indent}#{assignment}Class.new(Phronomy::Agent::Base) do\n"
|
|
83
|
-
new_lines << "#{indent} #{definition_line}\n"
|
|
84
|
-
# Rewrite the original body lines, stripping leading spaces
|
|
85
|
-
body.strip.split(";").each do |stmt|
|
|
86
|
-
stmt = stmt.strip
|
|
87
|
-
next if stmt.empty?
|
|
88
|
-
new_lines << "#{indent} #{stmt}\n"
|
|
89
|
-
end
|
|
90
|
-
new_lines << "#{indent}end\n"
|
|
91
|
-
modified = true
|
|
92
|
-
end
|
|
93
|
-
i += 1
|
|
94
|
-
next
|
|
95
|
-
end
|
|
96
|
-
|
|
97
|
-
new_lines << line
|
|
98
|
-
i += 1
|
|
99
|
-
end
|
|
100
|
-
|
|
101
|
-
if modified
|
|
102
|
-
File.write(file, new_lines.join)
|
|
103
|
-
files_modified += 1
|
|
104
|
-
puts " patched: #{file.sub("#{SPEC_DIR}/", "spec/")}"
|
|
105
|
-
end
|
|
106
|
-
end
|
|
107
|
-
|
|
108
|
-
puts "\nDone: #{counter} agent_definition insertions in #{files_modified} files."
|
|
@@ -1,53 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env ruby
|
|
2
|
-
# frozen_string_literal: true
|
|
3
|
-
|
|
4
|
-
# Second pass: handles patterns missed by the first script
|
|
5
|
-
# - Class.new(Phronomy::Agent::Base) { ... }.new (inline + .new suffix)
|
|
6
|
-
# - Class.new(Phronomy::Agent::Base).new (no block + .new)
|
|
7
|
-
# - { Class.new(Phronomy::Agent::Base) } (no block, inside outer { })
|
|
8
|
-
|
|
9
|
-
require "fileutils"
|
|
10
|
-
|
|
11
|
-
SPEC_DIR = File.expand_path("../spec", __dir__)
|
|
12
|
-
counter = 200
|
|
13
|
-
|
|
14
|
-
files_modified = 0
|
|
15
|
-
|
|
16
|
-
Dir.glob("#{SPEC_DIR}/**/*.rb").sort.each do |file|
|
|
17
|
-
content = File.read(file)
|
|
18
|
-
new_content = content.dup
|
|
19
|
-
|
|
20
|
-
# Pattern A: Class.new(Phronomy::Agent::Base) { body }.new
|
|
21
|
-
new_content = new_content.gsub(
|
|
22
|
-
/Class\.new\(Phronomy::Agent::Base\) \{([^}]*)\}\.new/
|
|
23
|
-
) do
|
|
24
|
-
body = $1
|
|
25
|
-
next $& if body.include?("agent_definition")
|
|
26
|
-
counter += 1
|
|
27
|
-
"Class.new(Phronomy::Agent::Base) { agent_definition id: \"test-agent-#{counter}\", version: 1;#{body}}.new"
|
|
28
|
-
end
|
|
29
|
-
|
|
30
|
-
# Pattern B: Class.new(Phronomy::Agent::Base).new (no block, chained .new)
|
|
31
|
-
new_content = new_content.gsub(
|
|
32
|
-
/Class\.new\(Phronomy::Agent::Base\)\.new\b/
|
|
33
|
-
) do
|
|
34
|
-
counter += 1
|
|
35
|
-
"Class.new(Phronomy::Agent::Base) { agent_definition id: \"test-agent-#{counter}\", version: 1 }.new"
|
|
36
|
-
end
|
|
37
|
-
|
|
38
|
-
# Pattern C: { Class.new(Phronomy::Agent::Base) } (no block, inside outer block)
|
|
39
|
-
new_content = new_content.gsub(
|
|
40
|
-
"{ Class.new(Phronomy::Agent::Base) }"
|
|
41
|
-
) do
|
|
42
|
-
counter += 1
|
|
43
|
-
"{ Class.new(Phronomy::Agent::Base) { agent_definition id: \"test-agent-#{counter}\", version: 1 } }"
|
|
44
|
-
end
|
|
45
|
-
|
|
46
|
-
if new_content != content
|
|
47
|
-
File.write(file, new_content)
|
|
48
|
-
files_modified += 1
|
|
49
|
-
puts " patched: #{file.sub("#{SPEC_DIR}/", "spec/")}"
|
|
50
|
-
end
|
|
51
|
-
end
|
|
52
|
-
|
|
53
|
-
puts "\nSecond pass done: #{files_modified} files modified."
|