agentf 0.4.6 → 0.4.7
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/lib/agentf/agents/debugger.rb +2 -2
- data/lib/agentf/agents/designer.rb +1 -1
- data/lib/agentf/agents/explorer.rb +2 -2
- data/lib/agentf/agents/security.rb +2 -2
- data/lib/agentf/agents/specialist.rb +1 -1
- data/lib/agentf/agents/tester.rb +1 -1
- data/lib/agentf/memory.rb +100 -15
- data/lib/agentf/version.rb +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 2cf35c1c3dd0c0b331de754c2805b3bbd27e9175a339ecb264509ab3ff5e843b
|
|
4
|
+
data.tar.gz: 1c552174bd8cfdfb0770f8a4c7ae19dd732c7c8e2d440f9197a5c210a0e55b1c
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: e60ee2faa826fe20c6845ab4250153838519456109893adcbba592e3a389a85520c00e3302a4bfb279e3b53b2c79c74afbe32a621a044837e904ca73dee37c54
|
|
7
|
+
data.tar.gz: d246b77664af5a645dc00afd17d0ea2799cf786d8c51f910212c7d24516e0f53a00bbeab81ec84b23a466d3a2185058bdbc89318aca932f20b7ef7c16be7e668
|
|
@@ -45,8 +45,8 @@ module Agentf
|
|
|
45
45
|
|
|
46
46
|
def self.policy_boundaries
|
|
47
47
|
{
|
|
48
|
-
"always" => ["Return analysis with root causes and suggested fix"
|
|
49
|
-
"ask_first" => ["Applying speculative fixes without reproducible error"],
|
|
48
|
+
"always" => ["Return analysis with root causes and suggested fix"],
|
|
49
|
+
"ask_first" => ["Applying speculative fixes without reproducible error", "Persisting debugging lessons to memory"],
|
|
50
50
|
"never" => ["Discard stack trace context when available"],
|
|
51
51
|
"required_inputs" => ["error_text"],
|
|
52
52
|
"required_outputs" => ["analysis", "success"]
|
|
@@ -46,7 +46,7 @@ module Agentf
|
|
|
46
46
|
def self.policy_boundaries
|
|
47
47
|
{
|
|
48
48
|
"always" => ["Return generated component details", "Persist successful implementation pattern"],
|
|
49
|
-
"ask_first" => ["Changing primary UI framework"],
|
|
49
|
+
"ask_first" => ["Changing primary UI framework", "Persisting successful implementation patterns to memory"],
|
|
50
50
|
"never" => ["Return empty generated code for successful design task"],
|
|
51
51
|
"required_inputs" => ["design_spec"],
|
|
52
52
|
"required_outputs" => ["component", "generated_code", "success"]
|
|
@@ -45,8 +45,8 @@ module Agentf
|
|
|
45
45
|
|
|
46
46
|
def self.policy_boundaries
|
|
47
47
|
{
|
|
48
|
-
"always" => ["Return concrete file evidence"
|
|
49
|
-
"ask_first" => ["Scanning outside configured base path"],
|
|
48
|
+
"always" => ["Return concrete file evidence"],
|
|
49
|
+
"ask_first" => ["Scanning outside configured base path", "Persisting exploration breadcrumbs to memory"],
|
|
50
50
|
"never" => ["Mutate project files during exploration"],
|
|
51
51
|
"required_inputs" => [],
|
|
52
52
|
"required_outputs" => ["files", "context_gathered"]
|
|
@@ -45,8 +45,8 @@ module Agentf
|
|
|
45
45
|
|
|
46
46
|
def self.policy_boundaries
|
|
47
47
|
{
|
|
48
|
-
"always" => ["Return issue list and best practices"
|
|
49
|
-
"ask_first" => ["Allowing known secret patterns in context"],
|
|
48
|
+
"always" => ["Return issue list and best practices"],
|
|
49
|
+
"ask_first" => ["Allowing known secret patterns in context", "Persisting security scan findings to memory"],
|
|
50
50
|
"never" => ["Echo raw secrets in output"],
|
|
51
51
|
"required_inputs" => ["task"],
|
|
52
52
|
"required_outputs" => ["issues", "best_practices"]
|
|
@@ -45,7 +45,7 @@ module Agentf
|
|
|
45
45
|
def self.policy_boundaries
|
|
46
46
|
{
|
|
47
47
|
"always" => ["Persist execution outcome", "Return deterministic success boolean"],
|
|
48
|
-
"ask_first" => ["Applying architecture style changes across unrelated modules"],
|
|
48
|
+
"ask_first" => ["Applying architecture style changes across unrelated modules", "Persisting execution outcomes to memory (success/pitfall)"] ,
|
|
49
49
|
"never" => ["Claim implementation complete without execution result"],
|
|
50
50
|
"required_inputs" => ["description"],
|
|
51
51
|
"required_outputs" => ["subtask_id", "success"]
|
data/lib/agentf/agents/tester.rb
CHANGED
|
@@ -46,7 +46,7 @@ module Agentf
|
|
|
46
46
|
def self.policy_boundaries
|
|
47
47
|
{
|
|
48
48
|
"always" => ["Produce framework-aware tests", "Verify red/green state when TDD enabled"],
|
|
49
|
-
"ask_first" => ["Changing test framework conventions"],
|
|
49
|
+
"ask_first" => ["Changing test framework conventions", "Persisting test-generation outcomes to memory"],
|
|
50
50
|
"never" => ["Mark passing when command output is uncertain"],
|
|
51
51
|
"required_inputs" => [],
|
|
52
52
|
"required_outputs" => ["test_file"]
|
data/lib/agentf/memory.rb
CHANGED
|
@@ -45,7 +45,39 @@ module Agentf
|
|
|
45
45
|
end
|
|
46
46
|
|
|
47
47
|
def store_episode(type:, title:, description:, context: "", code_snippet: "", tags: [], agent: Agentf::AgentRoles::ENGINEER,
|
|
48
|
-
related_task_id: nil, metadata: {}, entity_ids: [], relationships: [], parent_episode_id: nil, causal_from: nil)
|
|
48
|
+
related_task_id: nil, metadata: {}, entity_ids: [], relationships: [], parent_episode_id: nil, causal_from: nil, confirm: nil)
|
|
49
|
+
# Determine persistence preference from the agent's policy boundaries.
|
|
50
|
+
# Precedence: never > always > ask_first > none.
|
|
51
|
+
auto_confirm = ENV['AGENTF_AUTO_CONFIRM_MEMORIES'] == 'true'
|
|
52
|
+
pref = persistence_preference_for(agent)
|
|
53
|
+
|
|
54
|
+
case pref
|
|
55
|
+
when :never
|
|
56
|
+
begin
|
|
57
|
+
puts "[MEMORY] Skipping persistence for #{agent}: policy forbids persisting memories"
|
|
58
|
+
rescue StandardError
|
|
59
|
+
end
|
|
60
|
+
return nil
|
|
61
|
+
when :always
|
|
62
|
+
# proceed without requiring explicit confirm
|
|
63
|
+
when :ask_first
|
|
64
|
+
unless agent == Agentf::AgentRoles::ORCHESTRATOR || confirm == true || auto_confirm
|
|
65
|
+
begin
|
|
66
|
+
puts "[MEMORY] Skipping persistence for #{agent}: confirmation required by policy"
|
|
67
|
+
rescue StandardError
|
|
68
|
+
end
|
|
69
|
+
return nil
|
|
70
|
+
end
|
|
71
|
+
else
|
|
72
|
+
# default conservative behavior: require explicit confirm (or env opt-in)
|
|
73
|
+
unless agent == Agentf::AgentRoles::ORCHESTRATOR || confirm == true || auto_confirm
|
|
74
|
+
begin
|
|
75
|
+
puts "[MEMORY] Skipping persistence for #{agent}: confirmation required"
|
|
76
|
+
rescue StandardError
|
|
77
|
+
end
|
|
78
|
+
return nil
|
|
79
|
+
end
|
|
80
|
+
end
|
|
49
81
|
episode_id = "episode_#{SecureRandom.hex(4)}"
|
|
50
82
|
normalized_metadata = enrich_metadata(
|
|
51
83
|
metadata: metadata,
|
|
@@ -107,52 +139,56 @@ module Agentf
|
|
|
107
139
|
episode_id
|
|
108
140
|
end
|
|
109
141
|
|
|
110
|
-
def store_success(title:, description:, context: "", code_snippet: "", tags: [], agent: Agentf::AgentRoles::ENGINEER)
|
|
111
|
-
|
|
142
|
+
def store_success(title:, description:, context: "", code_snippet: "", tags: [], agent: Agentf::AgentRoles::ENGINEER, confirm: nil)
|
|
143
|
+
store_episode(
|
|
112
144
|
type: "success",
|
|
113
145
|
title: title,
|
|
114
146
|
description: description,
|
|
115
147
|
context: context,
|
|
116
148
|
code_snippet: code_snippet,
|
|
117
149
|
tags: tags,
|
|
118
|
-
|
|
150
|
+
agent: agent,
|
|
151
|
+
confirm: confirm
|
|
119
152
|
)
|
|
120
153
|
end
|
|
121
154
|
|
|
122
|
-
def store_pitfall(title:, description:, context: "", code_snippet: "", tags: [], agent: Agentf::AgentRoles::ENGINEER)
|
|
123
|
-
|
|
155
|
+
def store_pitfall(title:, description:, context: "", code_snippet: "", tags: [], agent: Agentf::AgentRoles::ENGINEER, confirm: nil)
|
|
156
|
+
store_episode(
|
|
124
157
|
type: "pitfall",
|
|
125
158
|
title: title,
|
|
126
159
|
description: description,
|
|
127
160
|
context: context,
|
|
128
161
|
code_snippet: code_snippet,
|
|
129
162
|
tags: tags,
|
|
130
|
-
|
|
163
|
+
agent: agent,
|
|
164
|
+
confirm: confirm
|
|
131
165
|
)
|
|
132
166
|
end
|
|
133
167
|
|
|
134
|
-
def store_lesson(title:, description:, context: "", code_snippet: "", tags: [], agent: Agentf::AgentRoles::ENGINEER)
|
|
135
|
-
|
|
168
|
+
def store_lesson(title:, description:, context: "", code_snippet: "", tags: [], agent: Agentf::AgentRoles::ENGINEER, confirm: nil)
|
|
169
|
+
store_episode(
|
|
136
170
|
type: "lesson",
|
|
137
171
|
title: title,
|
|
138
172
|
description: description,
|
|
139
173
|
context: context,
|
|
140
174
|
code_snippet: code_snippet,
|
|
141
175
|
tags: tags,
|
|
142
|
-
|
|
176
|
+
agent: agent,
|
|
177
|
+
confirm: confirm
|
|
143
178
|
)
|
|
144
179
|
end
|
|
145
180
|
|
|
146
|
-
def store_business_intent(title:, description:, constraints: [], tags: [], agent: Agentf::AgentRoles::ORCHESTRATOR, priority: 1)
|
|
181
|
+
def store_business_intent(title:, description:, constraints: [], tags: [], agent: Agentf::AgentRoles::ORCHESTRATOR, priority: 1, confirm: nil)
|
|
147
182
|
context = constraints.any? ? "Constraints: #{constraints.join('; ')}" : ""
|
|
148
183
|
|
|
149
|
-
|
|
184
|
+
store_episode(
|
|
150
185
|
type: "business_intent",
|
|
151
186
|
title: title,
|
|
152
187
|
description: description,
|
|
153
188
|
context: context,
|
|
154
189
|
tags: tags,
|
|
155
|
-
|
|
190
|
+
agent: agent,
|
|
191
|
+
confirm: confirm,
|
|
156
192
|
metadata: {
|
|
157
193
|
"intent_kind" => "business",
|
|
158
194
|
"constraints" => constraints,
|
|
@@ -162,18 +198,19 @@ module Agentf
|
|
|
162
198
|
end
|
|
163
199
|
|
|
164
200
|
def store_feature_intent(title:, description:, acceptance_criteria: [], non_goals: [], tags: [], agent: Agentf::AgentRoles::PLANNER,
|
|
165
|
-
|
|
201
|
+
related_task_id: nil, confirm: nil)
|
|
166
202
|
context_parts = []
|
|
167
203
|
context_parts << "Acceptance: #{acceptance_criteria.join('; ')}" if acceptance_criteria.any?
|
|
168
204
|
context_parts << "Non-goals: #{non_goals.join('; ')}" if non_goals.any?
|
|
169
205
|
|
|
170
|
-
|
|
206
|
+
store_episode(
|
|
171
207
|
type: "feature_intent",
|
|
172
208
|
title: title,
|
|
173
209
|
description: description,
|
|
174
210
|
context: context_parts.join(" | "),
|
|
175
211
|
tags: tags,
|
|
176
212
|
agent: agent,
|
|
213
|
+
confirm: confirm,
|
|
177
214
|
related_task_id: related_task_id,
|
|
178
215
|
metadata: {
|
|
179
216
|
"intent_kind" => "feature",
|
|
@@ -858,6 +895,54 @@ module Agentf
|
|
|
858
895
|
base
|
|
859
896
|
end
|
|
860
897
|
|
|
898
|
+
# Determine whether writes from the given agent should require explicit
|
|
899
|
+
# confirmation. We consider agents that declare "ask_first" policy
|
|
900
|
+
# boundaries to be conservative and require confirmation before persisting
|
|
901
|
+
# memories. Agent classes register policy boundaries on their class
|
|
902
|
+
# definitions (see agents/*). When agent is provided as a role string
|
|
903
|
+
# (eg. "ENGINEER") we try to match against known agent classes.
|
|
904
|
+
def agent_requires_confirmation?(agent)
|
|
905
|
+
begin
|
|
906
|
+
# If a developer passed an agent class-like string, try to map it to
|
|
907
|
+
# the loaded agent class and inspect its policy_boundaries.
|
|
908
|
+
candidate = Agentf::Agents.constants
|
|
909
|
+
.map { |c| Agentf::Agents.const_get(c) }
|
|
910
|
+
.find do |klass|
|
|
911
|
+
klass.is_a?(Class) && klass.respond_to?(:policy_boundaries) && klass.typed_name == agent
|
|
912
|
+
end
|
|
913
|
+
|
|
914
|
+
return false unless candidate
|
|
915
|
+
|
|
916
|
+
boundaries = candidate.policy_boundaries
|
|
917
|
+
ask_first = Array(boundaries["ask_first"]) rescue []
|
|
918
|
+
!ask_first.empty?
|
|
919
|
+
rescue StandardError
|
|
920
|
+
false
|
|
921
|
+
end
|
|
922
|
+
end
|
|
923
|
+
|
|
924
|
+
# Inspect loaded agent classes for explicit persistence preference.
|
|
925
|
+
# Returns one of: :always, :ask_first, :never, or nil when unknown.
|
|
926
|
+
def persistence_preference_for(agent)
|
|
927
|
+
begin
|
|
928
|
+
candidate = Agentf::Agents.constants
|
|
929
|
+
.map { |c| Agentf::Agents.const_get(c) }
|
|
930
|
+
.find do |klass|
|
|
931
|
+
klass.is_a?(Class) && klass.respond_to?(:policy_boundaries) && klass.typed_name == agent
|
|
932
|
+
end
|
|
933
|
+
|
|
934
|
+
return nil unless candidate
|
|
935
|
+
|
|
936
|
+
boundaries = candidate.policy_boundaries
|
|
937
|
+
return :never if Array(boundaries["never"]).any?
|
|
938
|
+
return :always if Array(boundaries["always"]).any? && Array(boundaries["ask_first"]).empty?
|
|
939
|
+
return :ask_first if Array(boundaries["ask_first"]).any?
|
|
940
|
+
nil
|
|
941
|
+
rescue StandardError
|
|
942
|
+
nil
|
|
943
|
+
end
|
|
944
|
+
end
|
|
945
|
+
|
|
861
946
|
def infer_division(agent)
|
|
862
947
|
case agent
|
|
863
948
|
when Agentf::AgentRoles::PLANNER, Agentf::AgentRoles::ORCHESTRATOR, Agentf::AgentRoles::KNOWLEDGE_MANAGER
|
data/lib/agentf/version.rb
CHANGED
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: agentf
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.4.
|
|
4
|
+
version: 0.4.7
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Neal Deters
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2026-03-
|
|
11
|
+
date: 2026-03-11 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: redis
|