Commit Graph

1750 Commits

Author SHA1 Message Date
Rodribm10
f3f8a8d5c1 feat(captain): rate limiting with runaway loop detection + bot_handoff
Três camadas de proteção contra runaway token burn no AgentRunnerService:

1. MAX_TURNS_PER_MESSAGE = 15
   Cap dentro de uma única chamada run(). Já estava aplicado;
   agora extraído como constante nomeada.

2. MAX_TURNS_PER_CONVERSATION = 30
   Cap ao longo da vida da conversa. Contador em
   conversation.custom_attributes['captain_turn_count']. Ao atingir,
   dispara bot_handoff automático e responde com mensagem de
   transferência pra humano.

3. TOOL_LOOP_THRESHOLD = 3
   Detecta a mesma (tool_name, args) invocada 3+ vezes no resultado
   de um único run (sintoma do loop faq_lookup que queimou tokens
   em 2026-04-19). Ao detectar: dispara bot_handoff e aborta o turno.

trigger_bot_handoff! aciona conversation.bot_handoff! quando
disponível, removendo a conversa do pipeline automático.

Motivação: dois incidentes reais de queima de crédito OpenAI em
2026-04-19. Ver memory/feedback_never_touch_captain_without_safety_caps.md
pras invariantes completas.

Tests atualizados: mock_result agora stuba :messages (usado pelo
novo tool_loop_detected?) e max_turns esperado é 15.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-19 11:16:54 -03:00
Rodribm10
6330bec857 fix(captain-memory): temporal memory model + aggressive dedup
User feedback revealed a fundamental design issue: the memory model was
accumulating contradictory "Prefere X" facts because a single choice was
being treated as a permanent preference. Result: 3 different
"Prefere suite X" entries coexisting, all at 90% confidence, with
reservation patterns over time (2hrs, 4hrs, pernoite) all claiming to be
the customer's "preferred" duration.

Corrections:

1. ExtractionService prompt — preferencia now requires EXPLICIT
  declaration words ("prefiro", "gosto mais de", "sempre escolho",
  "adoro", "favorita"). A mere choice in one conversation is NO LONGER
  extracted as preferencia — instead it goes to padrao_comportamental
  WITH THE DATE in the content (e.g. "Reservou Alexa para pernoite em
  23/05/2026"). This makes memory temporal and auditable instead of
  imposing fake consistency.

2. Reference date is passed to the LLM prompt via the latest message
  timestamp, used as the anchor date the LLM must embed in every
  padrao_comportamental content.

3. ContradictionCheckerService — dual threshold:
  - cosine < 0.15 → auto-supersede without LLM (pure duplicate)
  - 0.15 to 0.6 → ask LLM if contradicts, supersede if yes
  - > 0.6 → ignore, unrelated facts
  Previously only the middle band existed, so near-duplicate facts like
  two "aniversário 23/05" entries or three "prefere suite X" entries
  were never cleaned up.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-19 08:30:42 -03:00
Rodribm10
f7d4c41d07 feat(captain-memory): add MemoriesController with index/update/destroy/bulk_destroy 2026-04-19 01:41:09 -03:00
Rodribm10
638e84752d feat(captain-memory): add ContactMemoryPolicy (Pundit) 2026-04-19 01:37:13 -03:00
Rodribm10
9c035722de test(captain-memory): end-to-end learning and recall integration test 2026-04-19 01:35:09 -03:00
Rodribm10
1cf9531741 fix(captain-memory): use Agent#clone instead of ivar mutation + unify test path with runtime 2026-04-19 01:32:56 -03:00
Rodribm10
85324f594d feat(captain-memory): inject semantic memory into AgentRunnerService system prompt 2026-04-19 01:23:03 -03:00
Rodribm10
e89b96d09b feat(captain-memory): enqueue extraction on conversation.resolved 2026-04-19 01:13:26 -03:00
Rodribm10
2261b09b25 feat(captain-memory): add HardDeleteExpiredJob with daily cron (LGPD) 2026-04-19 01:09:28 -03:00
Rodribm10
b3077b2b26 feat(captain-memory): add AgingJob with TTL + LRU cap, weekly cron 2026-04-19 01:05:02 -03:00
Rodribm10
fb6673664a fix(captain-memory): isolate per-account failures in SilenceDetectorJob + fix typo 2026-04-19 01:01:28 -03:00
Rodribm10
833e76856e feat(captain-memory): add SilenceDetectorJob with 10min cron 2026-04-19 00:55:15 -03:00
Rodribm10
1646f66a97 fix(captain-memory): wrap ExtractFromConversationJob persistence in transaction + hoist unit lookup 2026-04-19 00:50:08 -03:00
Rodribm10
9d5e4c959f feat(captain-memory): add ExtractFromConversationJob with TTL + idempotency 2026-04-19 00:45:14 -03:00
Rodribm10
350a420ee0 feat(captain-memory): add ContradictionCheckerJob 2026-04-19 00:39:52 -03:00
Rodribm10
dc366433bb feat(captain-memory): add UpdateEmbeddingJob 2026-04-19 00:35:06 -03:00
Rodribm10
6723473fdc fix(captain-memory): ContradictionChecker exact-match parsing + rescue wrap + LLM failure test 2026-04-19 00:31:54 -03:00
Rodribm10
9bc6429b91 feat(captain-memory): add ContradictionCheckerService with LLM verification 2026-04-19 00:26:58 -03:00
Rodribm10
aec796ebfd fix(captain-memory): cap ExtractionService input, validate scope, filter failed msgs 2026-04-19 00:24:09 -03:00
Rodribm10
9d593757df feat(captain-memory): add ExtractionService with evidence+confidence guardrails 2026-04-19 00:18:32 -03:00
Rodribm10
0fee1b3c2f fix(captain-memory): strengthen RecallService logging context and document timeout tradeoff 2026-04-19 00:14:06 -03:00
Rodribm10
502c3d1698 feat(captain-memory): add RecallService with timeout and graceful degradation 2026-04-19 00:09:31 -03:00
Rodribm10
5d15f55a29 feat(captain-memory): add PromptInjectionService formatting memories as XML 2026-04-19 00:05:11 -03:00
Rodribm10
e1273f142b feat(captain-memory): add Captain::ContactMemory model with scopes and lifecycle methods 2026-04-18 23:53:33 -03:00
Rodribm10
2bf68e5be8 feat(captain-memory): add feature flag helpers on Account 2026-04-18 22:10:10 -03:00
Rodribm10
8ea87027d1 fix: move captain_unit_factory_spec out of factories/ (was breaking rails runner boot) 2026-04-15 22:19:48 -03:00
Rodribm10
fa1dd8b6cb feat(lifecycle): expose concierge config update on UnitsController
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-15 10:35:03 -03:00
Rodribm10
0b195781c5 feat(lifecycle): REST endpoint for lifecycle deliveries audit log 2026-04-15 10:29:24 -03:00
Rodribm10
8690a49971 feat(lifecycle): REST endpoint for lifecycle config singleton 2026-04-15 10:23:42 -03:00
Rodribm10
7c17a7cb96 feat(lifecycle): REST endpoint for lifecycle rules CRUD
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-15 10:17:59 -03:00
Rodribm10
fbc91e2fa8 feat(lifecycle): add REST routes for rules, config, deliveries, concierge
Wires 3 new captain namespace resources (lifecycle_rules, lifecycle_config,
lifecycle_deliveries) and a member action `patch :concierge` on units.
Includes stub controllers (to be expanded in Tasks 4-7) and passing routing spec.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-15 10:11:39 -03:00
Rodribm10
7d21530bc7 feat(lifecycle): add Pundit policies for rule/config/delivery 2026-04-15 10:06:47 -03:00
Rodribm10
b29b35465b feat(lifecycle): add Account associations for lifecycle models 2026-04-15 10:03:01 -03:00
Rodribm10
325f05c3eb fix(spec): captain_unit factory now auto-creates brand in matching account
Replaced broken `association :brand, factory: :captain_brand, account: account`
(FactoryBot cannot evaluate `account` lazily that way) with a transient block
that does `Captain::Brand.find_by(account_id: account.id) || association(...)`,
ensuring the brand always belongs to the same account as the unit.
Adds factory spec (6 examples) confirming standalone create, account override,
and brand reuse all work correctly.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-15 09:36:52 -03:00
Rodribm10
f302726d9b test(lifecycle): add end-to-end integration spec for scheduler→dispatch→send flow
Also fixes double-scheduling bug in scheduler_spec and delivery_spec caused by
after_create_commit hook firing while rules already exist — reservation is now
created before rules in setup so the hook finds nothing to schedule.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-15 09:29:52 -03:00
Rodribm10
7b009cf47f feat(lifecycle): inject concierge context into Captain orchestrator prompt
Adds concierge.* and reservation.* Liquid variables to agent_instructions
so Sofia's orchestrator_prompt receives unit persona/knowledge/variables
and reservation data resolved from conversation.custom_attributes.current_unit_id.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-15 09:25:16 -03:00
Rodribm10
d0d08ed662 feat(lifecycle): implement DispatcherJob
Replace no-op stub with full perform body: find delivery by id, skip if
blank, delegate to Captain::Lifecycle::Dispatcher#call. Add retry_on
with polynomially_longer backoff (3 attempts). Spec covers dispatcher
delegation and graceful skip for missing records.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-15 09:20:32 -03:00
Rodribm10
0d4583a21a feat(lifecycle): add Dispatcher service with guards→render→send pipeline
Orchestrates guards → render (Liquid) → send pipeline for one delivery.
Handles skip, reschedule, sent, failed states and re-enqueues on reschedule.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-15 01:53:01 -03:00
Rodribm10
6d84a7586b feat(lifecycle): add MinInterval and CustomerReplied guards
Implement guards following the same pass/reschedule/too_stale pattern as QuietHours.
Also fix belongs_to :conversation on Delivery to use class_name: '::Conversation' to avoid namespace resolution failure inside Captain::Lifecycle module.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-15 01:49:22 -03:00
Rodribm10
fcdc2054b5 feat(lifecycle): add QuietHours guard with 2h staleness limit
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-15 01:44:39 -03:00
Rodribm10
823008a1cd feat(lifecycle): add Guards::Base e 3 guards simples (ReservationActive, OptOutLabel, MaxPerReservation)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-15 01:42:10 -03:00
Rodribm10
f6aa39921a feat(lifecycle): add ContextBuilder for Liquid render variables
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-15 01:39:35 -03:00
Rodribm10
8e0a06246b feat(lifecycle): wire Captain::Reservation lifecycle hooks
Add after_commit callbacks to call Captain::Lifecycle::Scheduler on
create, status change (cancelled/no_show), and check_in_at change.
Each handler wraps in rescue StandardError to preserve existing behavior.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-15 01:37:23 -03:00
Rodribm10
bb4631f427 feat(lifecycle): add Scheduler service and DispatcherJob stub
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-15 01:35:31 -03:00
Rodribm10
4a88f7f517 feat(lifecycle): add EventResolver service
Pure function mapping reservation events to timestamps; used by Scheduler (T9) to compute fire_at.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-15 01:31:47 -03:00
Rodribm10
23a17599c4 feat(wuzapi): dispatch interactive messages (buttons/list/url_button)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-15 01:30:31 -03:00
Rodribm10
7a203ccb6d feat(wuzapi): add send_buttons, send_list, send_url_button methods
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-15 01:28:00 -03:00
Rodribm10
a4472b80b9 feat(lifecycle): add concierge_* accessors to Captain::Unit 2026-04-15 01:23:40 -03:00
Rodribm10
41bbf14d57 feat(lifecycle): add Captain::Lifecycle::Delivery model with state helpers
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-15 01:21:11 -03:00
Rodribm10
ffc5ac7fb8 feat(lifecycle): add Captain::Lifecycle::Rule model with filter matching
TDD: 16 examples passing. Adds EVENTS constant, active/for_event scopes,
and matches_reservation? with unit_ids/categorias/permanencias filters.
Also adds captain_reservation factory used by the spec.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-15 01:18:17 -03:00