agentf 0.5.0 → 0.7.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/lib/agentf/agents/architect.rb +3 -3
- data/lib/agentf/agents/base.rb +8 -23
- data/lib/agentf/agents/debugger.rb +1 -2
- data/lib/agentf/agents/designer.rb +22 -7
- data/lib/agentf/agents/documenter.rb +2 -2
- data/lib/agentf/agents/explorer.rb +1 -2
- data/lib/agentf/agents/reviewer.rb +7 -7
- data/lib/agentf/agents/security.rb +11 -9
- data/lib/agentf/agents/specialist.rb +28 -12
- data/lib/agentf/agents/tester.rb +22 -7
- data/lib/agentf/cli/eval.rb +1 -1
- data/lib/agentf/cli/memory.rb +95 -92
- data/lib/agentf/cli/router.rb +1 -1
- data/lib/agentf/commands/memory_reviewer.rb +21 -55
- data/lib/agentf/commands/metrics.rb +4 -13
- data/lib/agentf/context_builder.rb +4 -14
- data/lib/agentf/embedding_provider.rb +35 -0
- data/lib/agentf/installer.rb +162 -82
- data/lib/agentf/mcp/server.rb +123 -177
- data/lib/agentf/memory/confirmation_handler.rb +24 -0
- data/lib/agentf/memory.rb +322 -169
- data/lib/agentf/version.rb +1 -1
- data/lib/agentf/workflow_engine.rb +15 -18
- data/lib/agentf.rb +2 -0
- metadata +4 -2
data/lib/agentf/mcp/server.rb
CHANGED
|
@@ -20,6 +20,7 @@ module Agentf
|
|
|
20
20
|
# AGENTF_MCP_ALLOW_WRITES - true/false, controls memory write tools
|
|
21
21
|
# AGENTF_MCP_MAX_ARG_LENGTH - max length per string argument
|
|
22
22
|
class Server
|
|
23
|
+
include Agentf::Memory::ConfirmationHandler
|
|
23
24
|
ToolDefinition = Struct.new(:name, :description, :arguments, :handler, keyword_init: true)
|
|
24
25
|
|
|
25
26
|
class ToolBuilder
|
|
@@ -176,6 +177,7 @@ module Agentf
|
|
|
176
177
|
end
|
|
177
178
|
|
|
178
179
|
KNOWN_TOOLS = %w[
|
|
180
|
+
agentf-mcp-list-tools
|
|
179
181
|
agentf-code-glob
|
|
180
182
|
agentf-code-grep
|
|
181
183
|
agentf-code-tree
|
|
@@ -183,31 +185,25 @@ module Agentf
|
|
|
183
185
|
agentf-architecture-analyze-layers
|
|
184
186
|
agentf-memory-recent
|
|
185
187
|
agentf-memory-search
|
|
186
|
-
agentf-memory-by-tag
|
|
187
188
|
agentf-memory-by-agent
|
|
188
189
|
agentf-memory-by-type
|
|
189
|
-
agentf-memory-
|
|
190
|
-
agentf-memory-pitfalls
|
|
190
|
+
agentf-memory-episodes
|
|
191
191
|
agentf-memory-lessons
|
|
192
|
-
agentf-memory-successes
|
|
193
192
|
agentf-memory-intents
|
|
194
|
-
agentf-memory-
|
|
195
|
-
agentf-memory-feature-intents
|
|
193
|
+
agentf-memory-summary
|
|
196
194
|
agentf-memory-neighbors
|
|
197
195
|
agentf-memory-subgraph
|
|
196
|
+
agentf-memory-add-intent
|
|
197
|
+
agentf-memory-add-episode
|
|
198
|
+
agentf-memory-add-playbook
|
|
198
199
|
agentf-memory-add-lesson
|
|
199
|
-
agentf-memory-add-success
|
|
200
|
-
agentf-memory-add-pitfall
|
|
201
|
-
agentf-memory-add-business-intent
|
|
202
|
-
agentf-memory-add-feature-intent
|
|
203
200
|
].freeze
|
|
204
201
|
|
|
205
202
|
WRITE_TOOLS = Set.new(%w[
|
|
203
|
+
agentf-memory-add-intent
|
|
204
|
+
agentf-memory-add-episode
|
|
205
|
+
agentf-memory-add-playbook
|
|
206
206
|
agentf-memory-add-lesson
|
|
207
|
-
agentf-memory-add-success
|
|
208
|
-
agentf-memory-add-pitfall
|
|
209
|
-
agentf-memory-add-business-intent
|
|
210
|
-
agentf-memory-add-feature-intent
|
|
211
207
|
]).freeze
|
|
212
208
|
|
|
213
209
|
attr_reader :server, :guardrails
|
|
@@ -221,25 +217,6 @@ module Agentf
|
|
|
221
217
|
@server = build_server
|
|
222
218
|
end
|
|
223
219
|
|
|
224
|
-
# Helper to centralize confirmation handling for MCP server write tools.
|
|
225
|
-
# Yields the block that performs the memory write and returns either the
|
|
226
|
-
# block result or the normalized confirmation hash produced by
|
|
227
|
-
# Agentf::Agents::Base#safe_memory_write.
|
|
228
|
-
def safe_mcp_memory_write(memory, attempted: {})
|
|
229
|
-
begin
|
|
230
|
-
yield
|
|
231
|
-
nil
|
|
232
|
-
rescue Agentf::Memory::RedisMemory::ConfirmationRequired => e
|
|
233
|
-
{
|
|
234
|
-
"confirmation_required" => true,
|
|
235
|
-
"confirmation_details" => e.details,
|
|
236
|
-
"attempted" => attempted,
|
|
237
|
-
"confirmed_write_token" => "confirmed",
|
|
238
|
-
"confirmation_prompt" => "Ask the user whether to save this memory. If they approve, call the same tool again after confirmation. If they decline, do not retry."
|
|
239
|
-
}
|
|
240
|
-
end
|
|
241
|
-
end
|
|
242
|
-
|
|
243
220
|
# Start the stdio read loop (blocks until stdin closes).
|
|
244
221
|
def run
|
|
245
222
|
@server.run
|
|
@@ -392,23 +369,26 @@ module Agentf
|
|
|
392
369
|
end
|
|
393
370
|
|
|
394
371
|
s.tool("agentf-memory-search") do
|
|
395
|
-
description "Search memories
|
|
372
|
+
description "Search memories semantically. Supports optional filters for type, agent, and outcome."
|
|
396
373
|
argument :query, String, required: true, description: "Search query"
|
|
397
374
|
argument :limit, Integer, required: false, description: "How many results to return (1-100)"
|
|
375
|
+
argument :type, String, required: false, description: "Filter by type: episode|lesson|playbook|business_intent|feature_intent|incident"
|
|
376
|
+
argument :agent, String, required: false, description: "Filter by agent name"
|
|
377
|
+
argument :outcome, String, required: false, description: "Filter by outcome: positive|negative|neutral"
|
|
398
378
|
call do |args|
|
|
399
379
|
mcp_server.send(:guard!, "agentf-memory-search", **args)
|
|
400
|
-
result = reviewer.search(args[:query], limit: args[:limit] || 10)
|
|
380
|
+
result = reviewer.search(args[:query], limit: args[:limit] || 10, type: args[:type], agent: args[:agent], outcome: args[:outcome])
|
|
401
381
|
JSON.generate(result)
|
|
402
382
|
end
|
|
403
383
|
end
|
|
404
384
|
|
|
405
|
-
s.tool("agentf-memory-
|
|
406
|
-
description "
|
|
407
|
-
argument :
|
|
385
|
+
s.tool("agentf-memory-episodes") do
|
|
386
|
+
description "List episode memories with optional outcome filter."
|
|
387
|
+
argument :outcome, String, required: false, description: "Optional outcome filter: positive|negative|neutral"
|
|
408
388
|
argument :limit, Integer, required: false, description: "How many results to return (1-100)"
|
|
409
389
|
call do |args|
|
|
410
|
-
mcp_server.send(:guard!, "agentf-memory-
|
|
411
|
-
result = reviewer.
|
|
390
|
+
mcp_server.send(:guard!, "agentf-memory-episodes", **args)
|
|
391
|
+
result = reviewer.get_episodes(limit: args[:limit] || 10, outcome: args[:outcome])
|
|
412
392
|
JSON.generate(result)
|
|
413
393
|
end
|
|
414
394
|
end
|
|
@@ -435,25 +415,6 @@ module Agentf
|
|
|
435
415
|
end
|
|
436
416
|
end
|
|
437
417
|
|
|
438
|
-
s.tool("agentf-memory-tags") do
|
|
439
|
-
description "List all unique memory tags."
|
|
440
|
-
call do |args|
|
|
441
|
-
mcp_server.send(:guard!, "agentf-memory-tags", **args)
|
|
442
|
-
result = reviewer.get_all_tags
|
|
443
|
-
JSON.generate(result)
|
|
444
|
-
end
|
|
445
|
-
end
|
|
446
|
-
|
|
447
|
-
s.tool("agentf-memory-pitfalls") do
|
|
448
|
-
description "List pitfall memories."
|
|
449
|
-
argument :limit, Integer, required: false, description: "How many results to return (1-100)"
|
|
450
|
-
call do |args|
|
|
451
|
-
mcp_server.send(:guard!, "agentf-memory-pitfalls", **args)
|
|
452
|
-
result = reviewer.get_pitfalls(limit: args[:limit] || 10)
|
|
453
|
-
JSON.generate(result)
|
|
454
|
-
end
|
|
455
|
-
end
|
|
456
|
-
|
|
457
418
|
s.tool("agentf-memory-lessons") do
|
|
458
419
|
description "List lesson memories."
|
|
459
420
|
argument :limit, Integer, required: false, description: "How many results to return (1-100)"
|
|
@@ -464,18 +425,8 @@ module Agentf
|
|
|
464
425
|
end
|
|
465
426
|
end
|
|
466
427
|
|
|
467
|
-
s.tool("agentf-memory-successes") do
|
|
468
|
-
description "List success memories."
|
|
469
|
-
argument :limit, Integer, required: false, description: "How many results to return (1-100)"
|
|
470
|
-
call do |args|
|
|
471
|
-
mcp_server.send(:guard!, "agentf-memory-successes", **args)
|
|
472
|
-
result = reviewer.get_successes(limit: args[:limit] || 10)
|
|
473
|
-
JSON.generate(result)
|
|
474
|
-
end
|
|
475
|
-
end
|
|
476
|
-
|
|
477
428
|
s.tool("agentf-memory-intents") do
|
|
478
|
-
description "List intents (business|feature)."
|
|
429
|
+
description "List intents (business|feature). Pass kind to filter."
|
|
479
430
|
argument :kind, String, required: false, description: "Optional: business|feature"
|
|
480
431
|
argument :limit, Integer, required: false, description: "How many results to return (1-100)"
|
|
481
432
|
call do |args|
|
|
@@ -514,65 +465,85 @@ module Agentf
|
|
|
514
465
|
end
|
|
515
466
|
end
|
|
516
467
|
|
|
517
|
-
s.tool("agentf-memory-add-
|
|
518
|
-
description "Store a business intent
|
|
468
|
+
s.tool("agentf-memory-add-intent") do
|
|
469
|
+
description "Store a business or feature intent. Pass kind: business or feature."
|
|
470
|
+
argument :kind, String, required: true, description: "Intent type: business|feature"
|
|
519
471
|
argument :title, String, required: true, description: "Intent title"
|
|
520
472
|
argument :description, String, required: true, description: "Intent description"
|
|
521
|
-
argument :
|
|
522
|
-
argument :
|
|
523
|
-
argument :
|
|
473
|
+
argument :constraints, Array, required: false, items: String, description: "Business intent constraints"
|
|
474
|
+
argument :priority, Integer, required: false, description: "Business intent priority"
|
|
475
|
+
argument :acceptance, Array, required: false, items: String, description: "Feature intent acceptance criteria"
|
|
476
|
+
argument :non_goals, Array, required: false, items: String, description: "Feature intent non-goals"
|
|
477
|
+
argument :related_task_id, String, required: false, description: "Related task id (feature intents)"
|
|
524
478
|
call do |args|
|
|
525
|
-
mcp_server.send(:guard!, "agentf-memory-add-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
479
|
+
mcp_server.send(:guard!, "agentf-memory-add-intent", **args)
|
|
480
|
+
id = nil
|
|
481
|
+
kind = args[:kind].to_s.downcase
|
|
482
|
+
res = mcp_server.send(:safe_memory_write, memory, attempted: { tool: "agentf-memory-add-intent", args: args }) do
|
|
483
|
+
id = case kind
|
|
484
|
+
when "business"
|
|
485
|
+
memory.store_business_intent(
|
|
486
|
+
title: args[:title],
|
|
487
|
+
description: args[:description],
|
|
488
|
+
constraints: args[:constraints] || [],
|
|
489
|
+
priority: args[:priority] || 1
|
|
490
|
+
)
|
|
491
|
+
when "feature"
|
|
492
|
+
memory.store_feature_intent(
|
|
493
|
+
title: args[:title],
|
|
494
|
+
description: args[:description],
|
|
495
|
+
acceptance_criteria: args[:acceptance] || [],
|
|
496
|
+
non_goals: args[:non_goals] || [],
|
|
497
|
+
related_task_id: args[:related_task_id]
|
|
498
|
+
)
|
|
499
|
+
else
|
|
500
|
+
raise ArgumentError, "kind must be business or feature, got: #{kind}"
|
|
501
|
+
end
|
|
502
|
+
end
|
|
503
|
+
if res.is_a?(Hash) && res["confirmation_required"]
|
|
504
|
+
JSON.generate(confirmation_required: true, confirmation_details: res["confirmation_details"], attempted: res["attempted"])
|
|
505
|
+
else
|
|
506
|
+
JSON.generate(id: id, type: "#{kind}_intent", status: "stored")
|
|
507
|
+
end
|
|
544
508
|
end
|
|
545
509
|
end
|
|
546
510
|
|
|
547
|
-
s.tool("agentf-memory-add-
|
|
548
|
-
description "Store a
|
|
549
|
-
argument :
|
|
550
|
-
argument :
|
|
551
|
-
argument :
|
|
552
|
-
argument :
|
|
553
|
-
argument :
|
|
554
|
-
argument :
|
|
511
|
+
s.tool("agentf-memory-add-episode") do
|
|
512
|
+
description "Store a memory episode with type and outcome (type: episode|lesson|incident|playbook, outcome: positive|negative|neutral)."
|
|
513
|
+
argument :type, String, required: true, description: "Episode type: episode|lesson|incident|playbook"
|
|
514
|
+
argument :title, String, required: true, description: "Episode title"
|
|
515
|
+
argument :description, String, required: true, description: "Episode description"
|
|
516
|
+
argument :outcome, String, required: false, description: "Outcome: positive|negative|neutral"
|
|
517
|
+
argument :agent, String, required: false, description: "Agent name"
|
|
518
|
+
argument :context, String, required: false, description: "Additional context"
|
|
519
|
+
argument :code_snippet, String, required: false, description: "Code snippet"
|
|
555
520
|
call do |args|
|
|
556
|
-
mcp_server.send(:guard!, "agentf-memory-add-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
521
|
+
mcp_server.send(:guard!, "agentf-memory-add-episode", **args)
|
|
522
|
+
id = nil
|
|
523
|
+
res = mcp_server.send(:safe_memory_write, memory, attempted: { tool: "agentf-memory-add-episode", args: args }) do
|
|
524
|
+
id = memory.store_episode(
|
|
525
|
+
type: args[:type],
|
|
526
|
+
title: args[:title],
|
|
527
|
+
description: args[:description],
|
|
528
|
+
outcome: args[:outcome],
|
|
529
|
+
agent: args[:agent] || Agentf::AgentRoles::ENGINEER,
|
|
530
|
+
context: args[:context].to_s,
|
|
531
|
+
code_snippet: args[:code_snippet].to_s
|
|
532
|
+
)
|
|
533
|
+
end
|
|
534
|
+
if res.is_a?(Hash) && res["confirmation_required"]
|
|
535
|
+
JSON.generate(confirmation_required: true, confirmation_details: res["confirmation_details"], attempted: res["attempted"])
|
|
536
|
+
else
|
|
537
|
+
JSON.generate(id: id, type: args[:type], status: "stored")
|
|
538
|
+
end
|
|
539
|
+
end
|
|
540
|
+
end
|
|
569
541
|
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
end
|
|
542
|
+
s.tool("agentf-memory-summary") do
|
|
543
|
+
description "Get summary statistics: counts of memories by type, agent, and outcome."
|
|
544
|
+
call do |_args|
|
|
545
|
+
mcp_server.send(:guard!, "agentf-memory-summary")
|
|
546
|
+
JSON.generate(reviewer.get_summary)
|
|
576
547
|
end
|
|
577
548
|
end
|
|
578
549
|
|
|
@@ -612,24 +583,39 @@ module Agentf
|
|
|
612
583
|
end
|
|
613
584
|
end
|
|
614
585
|
|
|
586
|
+
s.tool("agentf-mcp-list-tools") do
|
|
587
|
+
description "List MCP tools and current guardrail status."
|
|
588
|
+
call do |_args|
|
|
589
|
+
# Use guard to ensure the caller is allowed to invoke tools
|
|
590
|
+
mcp_server.send(:guard!, "agentf-mcp-list-tools", **{})
|
|
591
|
+
|
|
592
|
+
tools = s.list_tools
|
|
593
|
+
guard = {
|
|
594
|
+
allowed_tools: mcp_server.guardrails[:allowed_tools].to_a,
|
|
595
|
+
allow_writes: mcp_server.guardrails[:allow_writes],
|
|
596
|
+
max_arg_length: mcp_server.guardrails[:max_arg_length]
|
|
597
|
+
}
|
|
598
|
+
|
|
599
|
+
JSON.generate({ tools: tools, guardrails: guard })
|
|
600
|
+
end
|
|
601
|
+
end
|
|
602
|
+
|
|
615
603
|
s.tool("agentf-memory-add-lesson") do
|
|
616
604
|
description "Store a lesson memory in Redis."
|
|
617
605
|
argument :title, String, required: true, description: "Lesson title"
|
|
618
606
|
argument :description, String, required: true, description: "Lesson description"
|
|
619
607
|
argument :agent, String, required: false, description: "Agent name"
|
|
620
|
-
argument :tags, Array, required: false, items: String, description: "Tags"
|
|
621
608
|
argument :context, String, required: false, description: "Context"
|
|
622
609
|
call do |args|
|
|
623
610
|
mcp_server.send(:guard!, "agentf-memory-add-lesson", **args)
|
|
624
611
|
begin
|
|
625
612
|
id = nil
|
|
626
|
-
res = mcp_server.send(:
|
|
613
|
+
res = mcp_server.send(:safe_memory_write, memory, attempted: { tool: "agentf-memory-add-lesson", args: args }) do
|
|
627
614
|
id = memory.store_episode(
|
|
628
615
|
type: "lesson",
|
|
629
616
|
title: args[:title],
|
|
630
617
|
description: args[:description],
|
|
631
618
|
agent: args[:agent] || Agentf::AgentRoles::ENGINEER,
|
|
632
|
-
tags: args[:tags] || [],
|
|
633
619
|
context: args[:context].to_s,
|
|
634
620
|
code_snippet: ""
|
|
635
621
|
)
|
|
@@ -650,64 +636,24 @@ module Agentf
|
|
|
650
636
|
end
|
|
651
637
|
end
|
|
652
638
|
|
|
653
|
-
s.tool("agentf-memory-add-
|
|
654
|
-
description "Store a
|
|
655
|
-
argument :title, String, required: true, description: "
|
|
656
|
-
argument :description, String, required: true, description: "
|
|
657
|
-
argument :agent, String, required: false, description: "Agent name"
|
|
658
|
-
argument :tags, Array, required: false, items: String, description: "Tags"
|
|
659
|
-
argument :context, String, required: false, description: "Context"
|
|
660
|
-
call do |args|
|
|
661
|
-
mcp_server.send(:guard!, "agentf-memory-add-success", **args)
|
|
662
|
-
begin
|
|
663
|
-
id = nil
|
|
664
|
-
res = mcp_server.send(:safe_mcp_memory_write, memory, attempted: { tool: "agentf-memory-add-success", args: args }) do
|
|
665
|
-
id = memory.store_episode(
|
|
666
|
-
type: "success",
|
|
667
|
-
title: args[:title],
|
|
668
|
-
description: args[:description],
|
|
669
|
-
agent: args[:agent] || Agentf::AgentRoles::ENGINEER,
|
|
670
|
-
tags: args[:tags] || [],
|
|
671
|
-
context: args[:context].to_s,
|
|
672
|
-
code_snippet: ""
|
|
673
|
-
)
|
|
674
|
-
end
|
|
675
|
-
|
|
676
|
-
if res.is_a?(Hash) && res["confirmation_required"]
|
|
677
|
-
JSON.generate(
|
|
678
|
-
confirmation_required: true,
|
|
679
|
-
confirmation_details: res["confirmation_details"],
|
|
680
|
-
attempted: res["attempted"],
|
|
681
|
-
confirmed_write_token: res["confirmed_write_token"],
|
|
682
|
-
confirmation_prompt: res["confirmation_prompt"]
|
|
683
|
-
)
|
|
684
|
-
else
|
|
685
|
-
JSON.generate(id: id, type: "success", status: "stored")
|
|
686
|
-
end
|
|
687
|
-
end
|
|
688
|
-
end
|
|
689
|
-
end
|
|
690
|
-
|
|
691
|
-
s.tool("agentf-memory-add-pitfall") do
|
|
692
|
-
description "Store a pitfall memory in Redis."
|
|
693
|
-
argument :title, String, required: true, description: "Pitfall title"
|
|
694
|
-
argument :description, String, required: true, description: "Pitfall description"
|
|
639
|
+
s.tool("agentf-memory-add-playbook") do
|
|
640
|
+
description "Store a playbook memory in Redis."
|
|
641
|
+
argument :title, String, required: true, description: "Playbook title"
|
|
642
|
+
argument :description, String, required: true, description: "Playbook description"
|
|
695
643
|
argument :agent, String, required: false, description: "Agent name"
|
|
696
|
-
argument :
|
|
697
|
-
argument :
|
|
644
|
+
argument :steps, Array, required: false, items: String, description: "Ordered playbook steps"
|
|
645
|
+
argument :feature_area, String, required: false, description: "Feature area"
|
|
698
646
|
call do |args|
|
|
699
|
-
mcp_server.send(:guard!, "agentf-memory-add-
|
|
647
|
+
mcp_server.send(:guard!, "agentf-memory-add-playbook", **args)
|
|
700
648
|
begin
|
|
701
649
|
id = nil
|
|
702
|
-
res = mcp_server.send(:
|
|
703
|
-
id = memory.
|
|
704
|
-
type: "pitfall",
|
|
650
|
+
res = mcp_server.send(:safe_memory_write, memory, attempted: { tool: "agentf-memory-add-playbook", args: args }) do
|
|
651
|
+
id = memory.store_playbook(
|
|
705
652
|
title: args[:title],
|
|
706
653
|
description: args[:description],
|
|
707
|
-
agent: args[:agent] || Agentf::AgentRoles::
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
code_snippet: ""
|
|
654
|
+
agent: args[:agent] || Agentf::AgentRoles::PLANNER,
|
|
655
|
+
steps: args[:steps] || [],
|
|
656
|
+
feature_area: args[:feature_area]
|
|
711
657
|
)
|
|
712
658
|
end
|
|
713
659
|
|
|
@@ -720,7 +666,7 @@ module Agentf
|
|
|
720
666
|
confirmation_prompt: res["confirmation_prompt"]
|
|
721
667
|
)
|
|
722
668
|
else
|
|
723
|
-
JSON.generate(id: id, type: "
|
|
669
|
+
JSON.generate(id: id, type: "playbook", status: "stored")
|
|
724
670
|
end
|
|
725
671
|
end
|
|
726
672
|
end
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Agentf
|
|
4
|
+
module Memory
|
|
5
|
+
module ConfirmationHandler
|
|
6
|
+
# Wraps a memory write block and normalizes ConfirmationRequired into a
|
|
7
|
+
# structured hash so callers (MCP server, CLI, agents) can handle it
|
|
8
|
+
# uniformly. The optional `_memory` arg is accepted for call-site
|
|
9
|
+
# readability but is not used by this method.
|
|
10
|
+
def safe_memory_write(_memory = nil, attempted: {})
|
|
11
|
+
yield
|
|
12
|
+
nil
|
|
13
|
+
rescue Agentf::Memory::RedisMemory::ConfirmationRequired => e
|
|
14
|
+
{
|
|
15
|
+
"confirmation_required" => true,
|
|
16
|
+
"confirmation_details" => e.details,
|
|
17
|
+
"attempted" => attempted,
|
|
18
|
+
"confirmed_write_token" => "confirmed",
|
|
19
|
+
"confirmation_prompt" => "Ask the user whether to save this memory. If they approve, rerun the same tool with confirmedWrite=confirmed. If they decline, do not retry."
|
|
20
|
+
}
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|