syntax = "proto3";
package mubit.v1;

service ControlService {
  // State Memory (Structured Variables)
  rpc SetVariable(SetVariableRequest) returns (Ack);
  rpc GetVariable(GetVariableRequest) returns (VariableResponse);
  rpc ListVariables(ListVariablesRequest) returns (ListVariablesResponse);
  rpc DeleteVariable(DeleteVariableRequest) returns (Ack);

  // Concepts (Level 4 Agency)
  rpc DefineConcept(DefineConceptRequest) returns (Ack);
  rpc ListConcepts(ListConceptsRequest) returns (ListConceptsResponse);

  // Goals
  rpc AddGoal(AddGoalRequest) returns (GoalResponse);
  rpc UpdateGoal(UpdateGoalRequest) returns (Ack);
  rpc ListGoals(ListGoalsRequest) returns (ListGoalsResponse);
  rpc GetGoalTree(GetGoalTreeRequest) returns (GetGoalTreeResponse);

  // Actions
  rpc SubmitAction(ActionRequest) returns (ActionResponse);
  rpc GetActionLog(ActionLogRequest) returns (ActionLogResponse);

  // Decision Cycle
  rpc RunCycle(RunCycleRequest) returns (CycleResponse);
  rpc GetCycleHistory(CycleHistoryRequest) returns (CycleHistoryResponse);

  // Management
  rpc DeleteRun(RunRequest) returns (Ack);

  // Events
  rpc Subscribe(SubscribeRequest) returns (stream Event);

  // Agent + control-plane primitives
  rpc RegisterAgent(AgentRegisterRequest) returns (AgentRegisterResponse);
  rpc AgentHeartbeat(AgentHeartbeatRequest) returns (Ack);
  rpc AppendActivity(ActivityAppendRequest) returns (ActivityAppendResponse);
  rpc GetRunSnapshot(RunSnapshotRequest) returns (RunSnapshotResponse);
  rpc LinkRun(LinkRunRequest) returns (LinkRunResponse);
  rpc UnlinkRun(UnlinkRunRequest) returns (UnlinkRunResponse);
  rpc Ingest(IngestRequest) returns (IngestAccepted);
  rpc BatchInsert(ControlBatchInsertRequest) returns (ControlBatchInsertResponse);
  rpc GetIngestJob(GetIngestJobRequest) returns (IngestJobResponse);
  rpc GetRunIngestStats(GetRunIngestStatsRequest) returns (RunIngestStatsResponse);
  rpc Query(AgentQueryRequest) returns (AgentQueryResponse);
  rpc Diagnose(DiagnoseRequest) returns (DiagnoseResponse);
  rpc Reflect(ReflectRequest) returns (ReflectResponse);

  // Lessons
  rpc ListLessons(ListLessonsRequest) returns (ListLessonsResponse);
  rpc DeleteLesson(DeleteLessonRequest) returns (Ack);

  // Context Block — pre-assembled memory context for LLM injection
  rpc GetContext(ContextRequest) returns (ContextResponse);

  // Exact archive primitive — store a reusable exact artifact block
  rpc ArchiveBlock(ArchiveBlockRequest) returns (ArchiveBlockResponse);

  // Exact dereference — read exact stored content by stable reference ID
  rpc Dereference(DereferenceRequest) returns (DereferenceResponse);

  // Chronological memory activity browse surface
  rpc ListActivity(ListActivityRequest) returns (ListActivityResponse);

  // Exact memory activity export surface
  rpc ExportActivity(ExportActivityRequest) returns (ExportActivityResponse);

  // Memory health — diagnostics for memory quality and lifecycle
  rpc GetMemoryHealth(MemoryHealthRequest) returns (MemoryHealthResponse);

  // Pre-compaction checkpoint — durable snapshot of context before compaction
  rpc Checkpoint(CheckpointRequest) returns (CheckpointResponse);

  // List registered agents for a run
  rpc ListAgents(ListAgentsRequest) returns (ListAgentsResponse);

  // Agent-to-agent coordination primitives
  rpc CreateHandoff(HandoffRequest) returns (HandoffResponse);
  rpc SubmitFeedback(FeedbackRequest) returns (FeedbackResponse);

  // Outcome feedback — record success/failure for a lesson or action
  rpc RecordOutcome(RecordOutcomeRequest) returns (RecordOutcomeResponse);

  // Strategy emergence — surface patterns from lesson clusters
  rpc SurfaceStrategies(SurfaceStrategiesRequest) returns (SurfaceStrategiesResponse);
}

message Ack {
  bool success = 1;
}

message RunRequest {
  string run_id = 1;
}

message SubscribeRequest {
  string run_id = 1;
  repeated string event_types = 2; // Optional filter
}

message Event {
  string id = 1;
  string type = 2; // "VariableUpdate", "GoalUpdate", etc.
  string run_id = 3;
  string agent_id = 4;
  string payload = 5; // JSON string of the object involved
  string created_at = 6;
}

message RunScope {
  string run_id = 1;
  repeated string linked_run_ids = 2;
  uint64 owner_user_id = 3;
}

message AgentIdentity {
  string agent_id = 1;
  string role = 2;
  repeated string capabilities = 3;
  string status = 4;
  string heartbeat_at = 5;
  // Memory scoping: which entry types this agent can read
  repeated string read_scopes = 6;
  // Memory scoping: which entry types this agent can write
  repeated string write_scopes = 7;
  // Shared memory lanes this agent participates in
  repeated string shared_memory_lanes = 8;
}

message ActivityRecord {
  string type = 1;
  string payload = 2;
  string tool_name = 3;
  string input_ref = 4;
  string output_ref = 5;
  string ts = 6;
  string agent_id = 7;
}

message RunSnapshot {
  string summary = 1;
  repeated string facts = 2;
  repeated string uncertainties = 3;
  repeated string blockers = 4;
  repeated string progress = 5;
  repeated string next_actions = 6;
  string updated_at = 7;
}

message PromotionDecision {
  string source_record_id = 1;
  string target = 2;
  float confidence = 3;
  string reason = 4;
  string policy_rule = 5;
  string created_at = 6;
}

message LlmOverride {
  string provider = 1;
  string model = 2;
  float temperature = 3;
  uint32 max_tokens = 4;
  uint64 timeout_ms = 5;
}

message ControlStatus {
  bool enabled = 1;
  bool healthy = 2;
  string provider = 3;
  string model = 4;
  string last_error = 5;
  string updated_at = 6;
}

message AgentRegisterRequest {
  string run_id = 1;
  string agent_id = 2;
  string role = 3;
  repeated string capabilities = 4;
  string status = 5;
  // Memory scoping: entry types this agent can read (empty = all)
  repeated string read_scopes = 6;
  // Memory scoping: entry types this agent can write (empty = all)
  repeated string write_scopes = 7;
  // Shared memory lanes this agent participates in
  repeated string shared_memory_lanes = 8;
}

message AgentRegisterResponse {
  bool success = 1;
  AgentIdentity agent = 2;
}

message AgentHeartbeatRequest {
  string run_id = 1;
  string agent_id = 2;
  string status = 3;
}

message ActivityAppendRequest {
  string run_id = 1;
  string agent_id = 2;
  ActivityRecord activity = 3;
  LlmOverride llm = 4;
}

message ActivityAppendResponse {
  bool success = 1;
  string event_id = 2;
}

message RunSnapshotRequest {
  string run_id = 1;
  uint64 timeline_limit = 2;
  bool refresh = 3;
  LlmOverride llm = 4;
}

message RunSnapshotResponse {
  RunScope scope = 1;
  RunSnapshot snapshot = 2;
  repeated AgentIdentity agents = 3;
  repeated Event timeline = 4;
  repeated PromotionDecision promotions = 5;
  ControlStatus control_status = 6;
}

message LinkRunRequest {
  string run_id = 1;
  string linked_run_id = 2;
}

message LinkRunResponse {
  bool success = 1;
  RunScope scope = 2;
}

message UnlinkRunRequest {
  string run_id = 1;
  string linked_run_id = 2;
}

message UnlinkRunResponse {
  bool success = 1;
  RunScope scope = 2;
}

message IngestItem {
  string item_id = 1;
  string content_type = 2;
  string text = 3;
  string payload_json = 4;
  string hints_json = 5;
  string metadata_json = 6;
  // Optional intent tag for structured routing (e.g. "observation", "tool_output",
  // "lesson", "rule", "handoff", "feedback", "context", "fact", "trace",
  // "log", "reflection", "task_result", "archive_block").
  // When present and not empty, bypasses LLM/heuristic classification.
  string intent = 7;
  // Lesson-specific fields (used when intent="lesson" or entry_type is lesson).
  // lesson_type: "success", "failure", "observation", "rule", "preference"
  string lesson_type = 8;
  // lesson_scope: "run", "session", "global"
  string lesson_scope = 9;
  // lesson_importance: "low", "medium", "high", "critical"
  string lesson_importance = 10;
  // lesson_conditions: JSON array of condition strings
  string lesson_conditions_json = 11;
  // Optional user/entity ID for multi-user memory scoping.
  // When set, memory is partitioned by this user within the run.
  string user_id = 12;
  // Optional upsert key. When set, an existing entry with the same key
  // in the (run_id, user_id) scope is updated instead of creating a new entry.
  string upsert_key = 13;
  // Importance level for this item (applies to all entry types, not just lessons).
  // Values: "low", "medium", "high", "critical". Default: auto-assigned based
  // on entry type when not provided.
  string importance = 14;
  // Ingestion source classification: "agent" (default), "human", or "system".
  string source = 20;
}

message IngestRequest {
  string run_id = 1;
  string agent_id = 2;
  string idempotency_key = 3;
  bool parallel = 4;
  repeated IngestItem items = 5;
}

message IngestAccepted {
  bool accepted = 1;
  string job_id = 2;
  bool deduplicated = 3;
  string status = 4;
}

message ControlBatchInsertItem {
  string item_id = 1;
  string text = 2;
  string metadata_json = 3;
  string source = 4;
  repeated float embedding = 5;
}

message ControlBatchInsertRequest {
  string run_id = 1;
  repeated ControlBatchInsertItem items = 2;
  bool deduplicate = 3;
}

message ControlBatchInsertItemResult {
  string item_id = 1;
  bool success = 2;
  uint64 node_id = 3;
  string error = 4;
}

message ControlBatchInsertResponse {
  bool success = 1;
  uint32 count = 2;
  repeated uint64 node_ids = 3;
  repeated ControlBatchInsertItemResult item_results = 4;
  string message = 5;
}

message GetIngestJobRequest {
  string run_id = 1;
  string job_id = 2;
}

message IngestWriteResult {
  string memory_type = 1;
  string record_id = 2;
  bool success = 3;
  string error = 4;
}

message IngestDecisionTrace {
  string item_id = 1;
  string content_type = 2;
  repeated string selected_types = 3;
  string reasoning = 4;
  string enriched_metadata_json = 5;
  repeated IngestWriteResult writes = 6;
  float confidence = 7;
}

message IngestJobResponse {
  string job_id = 1;
  string run_id = 2;
  string status = 3;
  bool done = 4;
  string error = 5;
  string created_at = 6;
  string started_at = 7;
  string finished_at = 8;
  repeated IngestDecisionTrace traces = 9;
  bool ordered = 10;
  bool deduplicated = 11;
}

message GetRunIngestStatsRequest {
  string run_id = 1;
}

message AgentIngestStats {
  string agent_id = 1;
  uint32 job_count = 2;
  uint32 completed_job_count = 3;
  uint32 failed_job_count = 4;
  uint32 deduplicated_job_count = 5;
  uint32 item_count = 6;
  string last_job_at = 7;
}

message RunIngestStatsResponse {
  string run_id = 1;
  uint32 total_jobs = 2;
  uint32 completed_jobs = 3;
  uint32 failed_jobs = 4;
  uint32 deduplicated_jobs = 5;
  uint32 total_items = 6;
  repeated AgentIngestStats agents = 7;
}

enum QueryMode {
  QUERY_MODE_UNSPECIFIED = 0;
  AGENT_ROUTED = 1;
  DIRECT_BYPASS = 2;
}

enum DirectQueryLane {
  DIRECT_QUERY_LANE_UNSPECIFIED = 0;
  SEMANTIC_SEARCH = 1;
}

message AgentQueryRequest {
  string run_id = 1;
  string query = 2;
  string schema = 3;
  QueryMode mode = 4;
  DirectQueryLane direct_lane = 5;
  bool include_linked_runs = 6;
  uint64 limit = 7;
  repeated float embedding = 8;
  // Optional LTM entry type filter (e.g. ["fact", "lesson", "trace"]).
  // When non-empty, only evidence matching these types is included.
  repeated string entry_types = 9;
  // Whether to include Working Memory variables/goals in evidence.
  // Defaults to true when not set.
  bool include_working_memory = 10;
  // Optional user/entity ID to scope query results to a specific user's memories.
  string user_id = 11;
  // Optional agent ID for scope-aware retrieval.
  // When set, evidence is filtered to only include types the agent
  // has declared in its read_scopes during registration.
  string agent_id = 12;
}

message QueryEvidence {
  string id = 1;
  string content = 2;
  string source = 3;
  float score = 4;
  string run_id = 5;
  // Entry type tag (e.g. "fact", "lesson", "working_memory", "goal")
  string entry_type = 6;
  // Serialized JSON metadata associated with this evidence item.
  string metadata_json = 7;
  // Retrieval provenance: semantic | exact_reference | checkpoint |
  // rule_overlay | lesson_overlay | archive_overlay | working_memory.
  string retrieval_mode = 8;
  // Stable reference ID when this evidence can be re-read exactly.
  string reference_id = 9;
  // Whether the evidence is exact-referenceable through Dereference.
  bool referenceable = 10;
  // Original stored entry type before any overlay/synthetic grouping.
  string origin_entry_type = 11;
}

message AgentQueryResponse {
  string final_answer = 1;
  repeated QueryEvidence evidence = 2;
  float confidence = 3;
  string mode = 4;
  bool degraded = 5;
  repeated string consulted_runs = 6;
  string routing_summary = 7;
}

message DiagnoseRequest {
  string run_id = 1;
  string error_text = 2;
  string error_type = 3;
  uint64 limit = 4;
  // Optional user/entity ID scope.
  string user_id = 5;
}

message DiagnoseResponse {
  repeated QueryEvidence failure_lessons = 1;
  string summary = 2;
  uint32 total_failure_lessons = 3;
}

// =============================================================================
// State Memory (Structured Variables)
// =============================================================================

message SetVariableRequest {
  string run_id = 1;
  string name = 2;
  string value_json = 3;  // JSON-encoded value
  string source = 4;      // "system", "reasoning", "retrieval", "perception", "explicit"
}

message GetVariableRequest {
  string run_id = 1;
  string name = 2;
}

message VariableResponse {
  string name = 1;
  string value_json = 2;
  string var_type = 3;
  string created_at = 4;
  string last_updated = 5;
  uint64 access_count = 6;
  string source = 7;
}

message ListVariablesRequest {
  string run_id = 1;
}

message ListVariablesResponse {
  repeated VariableResponse variables = 1;
}

message DeleteVariableRequest {
  string run_id = 1;
  string name = 2;
}

message DefineConceptRequest {
  string run_id = 1;
  string name = 2;
  string schema_json = 3;
}

message ListConceptsRequest {
  string run_id = 1;
}

message Concept {
  string name = 1;
  string schema_json = 2;
}

message ListConceptsResponse {
  repeated Concept concepts = 1;
}

// =============================================================================
// Goals
// =============================================================================

message AddGoalRequest {
  string run_id = 1;
  string description = 2;
  string priority = 3;    // "low", "medium", "high", "critical"
  string parent_id = 4;   // Optional parent goal ID
  string parent_goal_id = 5; // Optional parent goal ID (preferred alias)
}

message GoalResponse {
  string id = 1;
  string description = 2;
  string priority = 3;
  string status = 4;
  string parent_id = 5;
  string created_at = 6;
}

message UpdateGoalRequest {
  string run_id = 1;
  string goal_id = 2;
  string status = 3;      // "active", "suspended", "achieved", "abandoned", "failed"
}

message ListGoalsRequest {
  string run_id = 1;
  string status_filter = 2;  // Optional: filter by status
}

message ListGoalsResponse {
  repeated GoalResponse goals = 1;
}

message GetGoalTreeRequest {
  string run_id = 1;
  // Optional root goal ID. When unset, all root-level goals are returned.
  string root_goal_id = 2;
}

message GoalNode {
  GoalResponse goal = 1;
  repeated GoalNode children = 2;
  uint32 depth = 3;
}

message GetGoalTreeResponse {
  repeated GoalNode goals = 1;
}

// =============================================================================
// Actions
// =============================================================================

message ActionRequest {
  string run_id = 1;
  string agent_id = 2;
  string action_type = 3;  // "reason", "retrieve", "learn", "ground"
  string action_json = 4;  // JSON-encoded action details
}

message ActionResponse {
  string action_id = 1;
  bool success = 2;
  string result_json = 3;
  string error = 4;
  uint64 duration_ms = 5;
}

message ActionLogRequest {
  string run_id = 1;
  uint64 limit = 2;
  string action_type_filter = 3;  // Optional
}

message ActionLogResponse {
  repeated ActionLogEntry entries = 1;
}

message ActionLogEntry {
  string action_id = 1;
  string action_type = 2;
  string action_json = 3;
  bool success = 4;
  string result_json = 5;
  uint64 cycle_number = 6;
  string timestamp = 7;
}

// =============================================================================
// Decision Cycle
// =============================================================================

message RunCycleRequest {
  string run_id = 1;
  string agent_id = 2;
  repeated ActionCandidate candidates = 3;  // Optional: pre-proposed candidates
}

message ActionCandidate {
  string action_type = 1;
  string action_json = 2;
  float score = 3;
  string rationale = 4;
}

message CycleResponse {
  uint64 cycle_number = 1;
  ActionCandidate selected_action = 2;
  ActionResponse action_result = 3;
  uint64 planning_time_ms = 4;
  uint64 execution_time_ms = 5;
  string timestamp = 6;
}

message CycleHistoryRequest {
  string run_id = 1;
  uint64 limit = 2;
}

message CycleHistoryResponse {
  repeated CycleResponse cycles = 1;
}

// =============================================================================
// Reflection
// =============================================================================

message ReflectRequest {
  string run_id = 1;
  // Whether to include evidence from linked runs in the reflection.
  bool include_linked_runs = 2;
  // Optional user/entity ID to scope reflection to a specific user's memories.
  string user_id = 3;
}

message ReflectLesson {
  string content = 1;
  string lesson_type = 2;   // "success", "failure", "observation", "rule", "preference"
  string scope = 3;         // "run", "session", "global"
  string importance = 4;    // "low", "medium", "high", "critical"
  repeated string conditions = 5;
  string rationale = 6;
}

message ReflectResponse {
  repeated ReflectLesson lessons = 1;
  string summary = 2;
  float confidence = 3;
  bool degraded = 4;
  // Number of lessons that were stored in LTM.
  uint32 lessons_stored = 5;
}

// =============================================================================
// Lessons
// =============================================================================

message ListLessonsRequest {
  // When set, only return lessons from this run. When empty, return all lessons.
  string run_id = 1;
  // Filter by scope: "run", "session", "global". Empty = all scopes.
  string scope = 2;
  // Filter by importance: "low", "medium", "high", "critical". Empty = all.
  string importance = 3;
  uint64 limit = 4;
  // Optional user/entity ID to scope lessons to a specific user's memories.
  string user_id = 5;
}

message LessonEntry {
  string id = 1;
  string content = 2;
  string lesson_type = 3;
  string scope = 4;
  string importance = 5;
  repeated string conditions = 6;
  string rationale = 7;
  string source_run_id = 8;
  string source = 9;
}

message ListLessonsResponse {
  repeated LessonEntry lessons = 1;
}

message DeleteLessonRequest {
  string lesson_id = 1;
}

// =============================================================================
// Context Block — pre-assembled memory for LLM prompt injection
// =============================================================================

message ContextRequest {
  string run_id = 1;
  // Natural language query/topic to assemble context for.
  string query = 2;
  // Optional user/entity ID to scope context to.
  string user_id = 3;
  // Optional LTM entry type filter (e.g. ["fact", "lesson"]).
  repeated string entry_types = 4;
  // Whether to include Working Memory in the context block.
  bool include_working_memory = 5;
  // Output format: "structured" (default, section headers) or "compact".
  string format = 6;
  // Maximum number of evidence items per section.
  uint64 limit = 7;
  // Maximum token budget for the assembled context block.
  // When > 0, sections are filled in priority order (lessons → facts →
  // observations → working_memory → traces → goals) until the budget is
  // exhausted. Items within each section are ordered by descending score.
  // When 0 (default), no budget limit is applied.
  uint32 max_token_budget = 8;
  // Optional agent ID for scope-aware context assembly.
  string agent_id = 9;
  // Progressive disclosure mode:
  // - "full" (default): full context block + section summaries
  // - "summary": section summaries only (empty context_block)
  // - "sections": include only explicitly requested sections
  string mode = 10;
  // Section filter for mode="sections". Supported section keys:
  // active_rules, lessons, archive_blocks, handoffs, feedback, facts, observations,
  // working_memory, traces, goals, checkpoints, logs, other
  repeated string sections = 11;
}

message ContextSectionSummary {
  string section_name = 1;
  uint32 item_count = 2;
  string top_item_preview = 3;
  uint32 estimated_tokens = 4;
}

message ContextResponse {
  // Pre-assembled text block formatted for LLM prompt injection.
  // Stable ordering: lessons → facts → working_memory → traces → goals.
  string context_block = 1;
  // Estimated token count of the context block.
  uint32 token_estimate = 2;
  // Source evidence items used to build the block.
  repeated QueryEvidence sources = 3;
  // Actual token count used (equal to token_estimate when no budget limit).
  uint32 budget_used = 4;
  // Remaining budget (0 when no budget limit or budget fully used).
  uint32 budget_remaining = 5;
  // Per-section summaries to support progressive disclosure UX.
  repeated ContextSectionSummary section_summaries = 6;
  // Source counts by final entry type for the included context sources.
  map<string, uint32> source_counts_by_entry_type = 7;
  // Source counts by retrieval provenance mode for the included context sources.
  map<string, uint32> source_counts_by_retrieval_mode = 8;
  // Number of evidence candidates considered before token-budget truncation.
  uint32 evidence_candidates_considered = 9;
  // Number of evidence candidates dropped because of the token budget.
  uint32 evidence_dropped_by_budget = 10;
  // Number of included exact-referenceable sources in the final context block.
  uint32 exact_references_surfaced = 11;
}

// =============================================================================
// Exact archive blocks and dereference
// =============================================================================

message ArchiveBlockRequest {
  string run_id = 1;
  string content = 2;
  string artifact_kind = 3;
  string metadata_json = 4;
  string user_id = 5;
  string agent_id = 6;
  string origin_agent_id = 7;
  string source_attempt_id = 8;
  string source_tool = 9;
  repeated string labels = 10;
  string family = 11;
  string importance = 12;
}

message ArchiveBlockResponse {
  bool success = 1;
  string reference_id = 2;
  string entry_type = 3;
}

message DereferenceRequest {
  string run_id = 1;
  string reference_id = 2;
  string user_id = 3;
  string agent_id = 4;
}

message DereferenceResponse {
  bool found = 1;
  QueryEvidence evidence = 2;
}

// =============================================================================
// Chronological memory activity browse/export
// =============================================================================

message ListActivityRequest {
  // Optional run scope. When empty, activity is listed across visible runs for the actor.
  string run_id = 1;
  // Optional logical user scope.
  string user_id = 2;
  // Optional agent filter.
  string agent_id = 3;
  // Optional entry type filter.
  repeated string entry_types = 4;
  // Inclusive lower bound RFC3339 timestamp.
  string created_after = 5;
  // Inclusive upper bound RFC3339 timestamp.
  string created_before = 6;
  // Sort order: "desc" (default) or "asc".
  string sort = 7;
  // Page size. Defaults to 100, clamped to 500.
  uint32 limit = 8;
  // Opaque pagination token. Current implementation uses a numeric offset string.
  string page_token = 9;
}

message ActivityEntry {
  string id = 1;
  string run_id = 2;
  string user_id = 3;
  string actor_user_id = 4;
  string agent_id = 5;
  string entry_type = 6;
  string content = 7;
  string source = 8;
  string importance = 9;
  string created_at = 10;
  string metadata_json = 11;
  string reference_id = 12;
  bool referenceable = 13;
  string upsert_key = 14;
  string retrieval_mode = 15;
  string origin_entry_type = 16;
}

message ListActivityResponse {
  repeated ActivityEntry entries = 1;
  string next_page_token = 2;
  uint32 total_visible = 3;
}

message ExportActivityRequest {
  // Same filter semantics as ListActivityRequest.
  string run_id = 1;
  string user_id = 2;
  string agent_id = 3;
  repeated string entry_types = 4;
  string created_after = 5;
  string created_before = 6;
  string sort = 7;
}

message ExportActivityResponse {
  string format = 1;
  string content = 2;
  uint32 entry_count = 3;
}

// =============================================================================
// Memory Health
// =============================================================================

message MemoryHealthRequest {
  string run_id = 1;
  // Optional user/entity ID scope.
  string user_id = 2;
  // Staleness threshold in days. Defaults to 30 when <= 0.
  uint32 stale_threshold_days = 3;
  // Max entries to inspect. Defaults to 500 when <= 0.
  uint32 limit = 4;
}

message LessonContradiction {
  string lesson_a_id = 1;
  string lesson_b_id = 2;
  float similarity = 3;
  string lesson_a_type = 4;
  string lesson_b_type = 5;
}

message SectionHealth {
  string section_name = 1;
  uint32 count = 2;
  float avg_confidence = 3;
  float avg_age_hours = 4;
}

message MemoryHealthResponse {
  map<string, uint32> entry_counts = 1;
  uint32 stale_entries = 2;
  repeated LessonContradiction contradictions = 3;
  uint32 low_confidence_count = 4;
  repeated string promotion_candidates = 5;
  repeated SectionHealth section_health = 6;
}

// =============================================================================
// Pre-compaction Checkpoint
// =============================================================================

message CheckpointRequest {
  string run_id = 1;
  // Optional label for this checkpoint (e.g. "pre-compaction-3").
  string label = 2;
  // The full context window content being checkpointed.
  string context_snapshot = 3;
  // Structured metadata (JSON): agent states, tool results, etc.
  string metadata_json = 4;
  // Optional user/entity ID scope.
  string user_id = 5;
  // Optional agent_id attribution.
  string agent_id = 6;
}

message CheckpointResponse {
  bool success = 1;
  string checkpoint_id = 2;
  uint32 token_estimate = 3;
}

// =============================================================================
// Agent Discovery
// =============================================================================

message ListAgentsRequest {
  string run_id = 1;
}

message ListAgentsResponse {
  repeated AgentIdentity agents = 1;
}

message HandoffRequest {
  string run_id = 1;
  string task_id = 2;
  string from_agent_id = 3;
  string to_agent_id = 4;
  string content = 5;
  // review|continue|approve|execute
  string requested_action = 6;
  // Optional metadata payload.
  string metadata_json = 7;
  // Optional user/entity ID scope.
  string user_id = 8;
}

message HandoffResponse {
  bool success = 1;
  string handoff_id = 2;
}

message FeedbackRequest {
  string run_id = 1;
  string handoff_id = 2;
  // approve|request_changes|block|acknowledge
  string verdict = 3;
  string comments = 4;
  // Optional responding agent ID.
  string from_agent_id = 5;
  // Optional metadata payload.
  string metadata_json = 6;
  // Optional user/entity ID scope.
  string user_id = 7;
}

message FeedbackResponse {
  bool success = 1;
  string feedback_id = 2;
}

// =============================================================================
// Outcome Feedback (RL-like learning loop)
// =============================================================================

message RecordOutcomeRequest {
  string run_id = 1;
  // The lesson ID or action ID this outcome pertains to.
  string reference_id = 2;
  // "success", "failure", "partial", "neutral"
  string outcome = 3;
  // Numeric signal: positive = reward, negative = penalty, range [-1.0, 1.0]
  float signal = 4;
  // Human or agent-provided rationale for the outcome.
  string rationale = 5;
  // Optional agent attribution.
  string agent_id = 6;
  // Optional user scope.
  string user_id = 7;
}

message RecordOutcomeResponse {
  bool success = 1;
  // Updated reinforcement count for the referenced lesson.
  int32 reinforcement_count = 2;
  // Current confidence score after reinforcement.
  float updated_confidence = 3;
}

// =============================================================================
// Strategy Emergence (pattern discovery from lesson clusters)
// =============================================================================

message SurfaceStrategiesRequest {
  string run_id = 1;
  // Optional: limit to specific lesson types ("success", "failure", etc.)
  repeated string lesson_types = 2;
  // Maximum number of strategies to return.
  uint32 max_strategies = 3;
  // Optional user scope.
  string user_id = 4;
}

message EmergentStrategy {
  // Auto-generated strategy ID.
  string strategy_id = 1;
  // Human-readable strategy description synthesized from lesson cluster.
  string description = 2;
  // Number of lessons supporting this strategy.
  uint32 supporting_lesson_count = 3;
  // Average confidence across supporting lessons.
  float avg_confidence = 4;
  // Average reinforcement count across supporting lessons.
  float avg_reinforcement = 5;
  // Dominant lesson type in the cluster.
  string dominant_lesson_type = 6;
  // Dominant scope of supporting lessons.
  string dominant_scope = 7;
  // IDs of the lessons that form this strategy cluster.
  repeated string lesson_ids = 8;
}

message SurfaceStrategiesResponse {
  repeated EmergentStrategy strategies = 1;
}
