diff --git a/cmd/hap/main.go b/cmd/hap/main.go index 9d7279c..f3ddd7f 100644 --- a/cmd/hap/main.go +++ b/cmd/hap/main.go @@ -5,6 +5,7 @@ import ( "os" "path/filepath" + "chorus/internal/hapui" "chorus/internal/runtime" ) @@ -96,30 +97,14 @@ func startHAPMode(runtime *runtime.SharedRuntime) error { // startTerminalInterface provides a terminal-based human interface func startTerminalInterface(runtime *runtime.SharedRuntime) error { runtime.Logger.Info("πŸ’» Starting terminal interface for human interaction") - runtime.Logger.Info("🎯 Human agent ready for collaboration") - // TODO Phase 2: Implement terminal interface - // For now, just announce presence and wait - runtime.Logger.Info("πŸ“‘ Human agent announcing presence to network...") + // Create and start the HAP terminal interface + terminal := hapui.NewTerminalInterface(runtime) - // Announce human agent capabilities - go func() { - // TODO: Implement human agent announcement - runtime.Logger.Info("πŸ‘‹ Human agent presence announced") - }() - - // TODO Phase 2: Implement interactive terminal loop - // - HMMM message composition - // - Context browsing - // - Decision participation - // - Command interface - - runtime.Logger.Info("⚠️ Terminal interface not yet implemented") - runtime.Logger.Info("πŸ”„ HAP running in stub mode - P2P connectivity established") - runtime.Logger.Info("πŸ“ Next: Implement Phase 2 terminal interface") - - // For now, just keep the P2P connection alive - select {} // Block forever (will be interrupted by shutdown signals) + runtime.Logger.Info("🎯 Human agent terminal interface ready") + + // Start the interactive terminal + return terminal.Start() } // startWebInterface provides a web-based human interface diff --git a/docker/docker-compose.prompts.dev.yml b/docker/docker-compose.prompts.dev.yml new file mode 100644 index 0000000..82c48da --- /dev/null +++ b/docker/docker-compose.prompts.dev.yml @@ -0,0 +1,36 @@ +version: "3.9" + +services: + chorus-agent: + # For local dev, build from repo Dockerfile; alternatively set a pinned image tag + build: + context: .. + dockerfile: docker/Dockerfile + # image: registry.home.deepblack.cloud/chorus/agent:0.1.0 + container_name: chorus-agent-dev + env_file: + - ./chorus.env + environment: + # Prompt sourcing (mounted volume) + CHORUS_PROMPTS_DIR: /etc/chorus/prompts + CHORUS_DEFAULT_INSTRUCTIONS_PATH: /etc/chorus/prompts/defaults.md + CHORUS_ROLE: arbiter # change to your role id (e.g., hmmm-analyst) + + # Minimal AI provider config (ResetData example) + CHORUS_AI_PROVIDER: resetdata + RESETDATA_BASE_URL: https://models.au-syd.resetdata.ai/v1 + # Set RESETDATA_API_KEY via ./chorus.env or secrets manager + + # Required license id (bind or inject via env_file) + CHORUS_LICENSE_ID: ${CHORUS_LICENSE_ID} + + volumes: + # Mount prompts directory read-only + - ../prompts:/etc/chorus/prompts:ro + ports: + - "8080:8080" # API + - "8081:8081" # Health + - "9000:9000" # P2P + restart: unless-stopped + # profiles: [prompts] + diff --git a/docker/docker-compose.yml b/docker/docker-compose.yml index 2e33a80..2b2807b 100644 --- a/docker/docker-compose.yml +++ b/docker/docker-compose.yml @@ -47,6 +47,11 @@ services: - CHORUS_BACKBEAT_CLUSTER_ID=${CHORUS_BACKBEAT_CLUSTER_ID:-chorus-production} - CHORUS_BACKBEAT_AGENT_ID=${CHORUS_BACKBEAT_AGENT_ID:-} # Auto-generated from CHORUS_AGENT_ID - CHORUS_BACKBEAT_NATS_URL=${CHORUS_BACKBEAT_NATS_URL:-nats://backbeat-nats:4222} + + # Prompt sourcing (mounted volume) + - CHORUS_PROMPTS_DIR=/etc/chorus/prompts + - CHORUS_DEFAULT_INSTRUCTIONS_PATH=/etc/chorus/prompts/defaults.md + - CHORUS_ROLE=${CHORUS_ROLE:-arbiter} # Docker secrets for sensitive configuration secrets: @@ -55,6 +60,8 @@ services: # Persistent data storage volumes: - chorus_data:/app/data + # Mount prompts directory read-only for role YAMLs and defaults.md + - ../prompts:/etc/chorus/prompts:ro # Network ports ports: diff --git a/docs/decisions/2025-09-06-convert-human-prompts-to-roles-yaml.md b/docs/decisions/2025-09-06-convert-human-prompts-to-roles-yaml.md new file mode 100644 index 0000000..e35b379 --- /dev/null +++ b/docs/decisions/2025-09-06-convert-human-prompts-to-roles-yaml.md @@ -0,0 +1,46 @@ +# Decision Record: Convert Human Markdown Prompts to CHORUS Role YAML + +- Date: 2025-09-06 +- UCXL Address: ucxl://arbiter:ops@CHORUS:prompt-migration/#/docs/decisions/2025-09-06-convert-human-prompts-to-roles-yaml.md + +## Problem +Human-oriented prompt templates exist as Markdown files under `agentic-ai-prompt-templates/human/`. CHORUS now sources agent role prompts (S) and default instructions (D) at runtime from bind-mounted YAML/Markdown files. We need these human templates available in the new YAML format to configure agents via Docker volume binding without rebuilding images. + +## Options Considered +1) Manual conversion of each Markdown file to a YAML role entry + - Pros: Tight editorial control + - Cons: Time-intensive, error-prone, hard to keep in sync + +2) Automated converter script to parse Markdown sections and emit a consolidated `system_prompt` with metadata + - Pros: Fast, repeatable, easy to re-run when templates change + - Cons: Heuristics may miss atypical structures; requires review + +3) Store raw Markdown and embed at runtime + - Pros: No conversion step + - Cons: Diverges from adopted loader schema, complicates composition and validation + +## Decision +Adopt Option 2. Add a utility script `utilities/convert_human_prompts_to_yaml.py` that: +- Reads `agentic-ai-prompt-templates/human/*.md` +- Extracts title, Description, Tools, Use Cases, When to Use +- Constructs `system_prompt` as: "You are ." + Description + Tools + Use Cases + When To Use +- Emits `project-queues/active/CHORUS/prompts/human-roles.yaml` with one role per file, using filename as role ID +- Populates advisory `defaults` (models/capabilities/expertise/max_tasks) + +## Impact +- Roles become mountable via `CHORUS_PROMPTS_DIR` (e.g., `-v ../prompts:/etc/chorus/prompts:ro`) +- Agents can select any converted role via `CHORUS_ROLE=` +- Future updates to human Markdown can be re-converted by re-running the script + +## Rollback +- Remove `human-roles.yaml` from the prompts directory +- Agents will continue to use existing roles (`roles.yaml`) or default instructions only + +## Compatibility Notes +- Loader merges by role ID; ensure IDs don’t collide with existing `roles.yaml` (IDs are based on filenames) +- `defaults.md` remains the global instruction source and is unchanged by this migration + +## Evidence / References +- Loader & schema: `pkg/prompt/types.go`, `pkg/prompt/loader.go` +- Prompts directory & compose: `prompts/README.md`, `docker/docker-compose.prompts.dev.yml` + diff --git a/internal/hapui/terminal.go b/internal/hapui/terminal.go new file mode 100644 index 0000000..5b5d1e7 --- /dev/null +++ b/internal/hapui/terminal.go @@ -0,0 +1,2415 @@ +package hapui + +import ( + "bufio" + "context" + "crypto/rand" + "encoding/json" + "fmt" + "math/big" + "os" + "strconv" + "strings" + "time" + + "chorus/internal/runtime" + "chorus/pkg/hmmm" + "chorus/pkg/ucxl" + "chorus/pkg/storage" + "chorus/pubsub" +) + +// TerminalInterface provides an interactive terminal interface for human agents +type TerminalInterface struct { + runtime *runtime.SharedRuntime + scanner *bufio.Scanner + quit chan bool +} + +// NewTerminalInterface creates a new terminal interface for HAP +func NewTerminalInterface(runtime *runtime.SharedRuntime) *TerminalInterface { + return &TerminalInterface{ + runtime: runtime, + scanner: bufio.NewScanner(os.Stdin), + quit: make(chan bool), + } +} + +// Start begins the interactive terminal session +func (t *TerminalInterface) Start() error { + t.printWelcomeMessage() + t.printHelp() + + // Announce human agent presence + if err := t.announceHumanAgent(); err != nil { + t.runtime.Logger.Error("Failed to announce human agent presence: %v", err) + } + + // Start command processing loop + go t.commandLoop() + + // Wait for quit signal + <-t.quit + return nil +} + +// Stop terminates the terminal interface +func (t *TerminalInterface) Stop() { + close(t.quit) +} + +// printWelcomeMessage displays the HAP welcome screen +func (t *TerminalInterface) printWelcomeMessage() { + fmt.Println("\n" + strings.Repeat("=", 80)) + fmt.Println("🎭 CHORUS Human Agent Portal (HAP) - Terminal Interface") + fmt.Println(strings.Repeat("=", 80)) + fmt.Printf("Agent ID: %s\n", t.runtime.Config.Agent.ID) + fmt.Printf("P2P Node: %s\n", t.runtime.Node.ID().ShortString()) + fmt.Printf("Connected to: %d peers\n", t.runtime.Node.ConnectedPeers()) + fmt.Println("\nYou are now connected to the CHORUS P2P agent network as a human participant.") + fmt.Println("You can collaborate with autonomous agents using the same protocols.") + fmt.Println(strings.Repeat("=", 80) + "\n") +} + +// printHelp displays available commands +func (t *TerminalInterface) printHelp() { + fmt.Println("Available Commands:") + fmt.Println(" help - Show this help message") + fmt.Println(" status - Show network and agent status") + fmt.Println(" peers - List connected P2P peers") + fmt.Println(" hmmm - Compose and send HMMM reasoning message") + fmt.Println(" ucxl
- Browse UCXL context address") + fmt.Println(" patch - Create and submit patches") + fmt.Println(" decide - Participate in distributed decision") + fmt.Println(" announce - Re-announce human agent presence") + fmt.Println(" quit - Exit HAP terminal") + fmt.Println() +} + +// commandLoop handles user input and command processing +func (t *TerminalInterface) commandLoop() { + for { + fmt.Print("hap> ") + + if !t.scanner.Scan() { + // EOF or error + break + } + + input := strings.TrimSpace(t.scanner.Text()) + if input == "" { + continue + } + + parts := strings.Fields(input) + command := strings.ToLower(parts[0]) + + switch command { + case "help", "h": + t.printHelp() + + case "status", "s": + t.printStatus() + + case "peers": + t.listPeers() + + case "hmmm", "m": + t.handleHMMMCommand(parts[1:]) + + case "ucxl", "u": + if len(parts) < 2 { + fmt.Println("Usage: ucxl
") + continue + } + t.handleUCXLCommand(parts[1]) + + case "patch", "p": + t.handlePatchCommand() + + case "decide", "d": + if len(parts) < 2 { + fmt.Println("Usage: decide ") + continue + } + topic := strings.Join(parts[1:], " ") + t.handleDecisionCommand(topic) + + case "announce", "a": + if err := t.announceHumanAgent(); err != nil { + fmt.Printf("Failed to announce presence: %v\n", err) + } else { + fmt.Println("βœ… Human agent presence announced to network") + } + + case "quit", "q", "exit": + fmt.Println("πŸ‘‹ Goodbye! Disconnecting from CHORUS network...") + t.quit <- true + return + + case "clear", "cls": + // Clear screen (works on most terminals) + fmt.Print("\033[2J\033[H") + t.printWelcomeMessage() + + default: + fmt.Printf("Unknown command: %s\n", command) + fmt.Println("Type 'help' for available commands.") + } + } +} + +// printStatus displays current network and agent status +func (t *TerminalInterface) printStatus() { + fmt.Println("\nπŸ“Š HAP Status Report") + fmt.Println(strings.Repeat("-", 40)) + + // Agent Info + fmt.Printf("Agent ID: %s\n", t.runtime.Config.Agent.ID) + fmt.Printf("Agent Role: %s\n", t.runtime.Config.Agent.Role) + fmt.Printf("Agent Type: Human (HAP)\n") + + // P2P Network Status + fmt.Printf("Node ID: %s\n", t.runtime.Node.ID().ShortString()) + fmt.Printf("Connected Peers: %d\n", t.runtime.Node.ConnectedPeers()) + + // Task Status + activeTasks := t.runtime.TaskTracker.GetActiveTasks() + maxTasks := t.runtime.TaskTracker.GetMaxTasks() + fmt.Printf("Active Tasks: %d/%d\n", len(activeTasks), maxTasks) + + // DHT Status + if t.runtime.DHTNode != nil { + fmt.Printf("DHT: βœ… Connected\n") + } else { + fmt.Printf("DHT: ❌ Disabled\n") + } + + // BACKBEAT Status + if t.runtime.BackbeatIntegration != nil { + health := t.runtime.BackbeatIntegration.GetHealth() + if connected, ok := health["connected"].(bool); ok && connected { + fmt.Printf("BACKBEAT: βœ… Connected\n") + } else { + fmt.Printf("BACKBEAT: ⚠️ Disconnected\n") + } + } else { + fmt.Printf("BACKBEAT: ❌ Disabled\n") + } + + fmt.Println(strings.Repeat("-", 40)) + fmt.Printf("Last Updated: %s\n\n", time.Now().Format("15:04:05")) +} + +// listPeers displays connected P2P peers +func (t *TerminalInterface) listPeers() { + peerCount := t.runtime.Node.ConnectedPeers() + fmt.Printf("\nπŸ”— Connected P2P Peers (%d)\n", peerCount) + fmt.Println(strings.Repeat("-", 50)) + + if peerCount == 0 { + fmt.Println("No peers connected.") + fmt.Println("Ensure other CHORUS agents are running on the network.") + } else { + fmt.Printf("Connected to %d peer(s) in the CHORUS network.\n", peerCount) + fmt.Println("Use P2P discovery mechanisms to find autonomous agents.") + } + fmt.Println() +} + +// announceHumanAgent broadcasts human agent presence to the network +func (t *TerminalInterface) announceHumanAgent() error { + presence := map[string]interface{}{ + "agent_id": t.runtime.Config.Agent.ID, + "node_id": t.runtime.Node.ID().ShortString(), + "agent_type": "human", + "interface": "terminal", + "capabilities": []string{ + "hmmm_reasoning", + "decision_making", + "context_browsing", + "collaborative_editing", + }, + "human_operator": true, + "timestamp": time.Now().Unix(), + "status": "online", + } + + // Publish to capability broadcast topic + if err := t.runtime.PubSub.PublishBzzzMessage(pubsub.CapabilityBcast, presence); err != nil { + return fmt.Errorf("failed to publish human agent announcement: %w", err) + } + + t.runtime.Logger.Info("πŸ‘€ Human agent presence announced to network") + return nil +} + +// handleHMMMCommand processes HMMM reasoning message composition +func (t *TerminalInterface) handleHMMMCommand(args []string) { + fmt.Println("\nπŸ“ HMMM (Human-Machine-Machine-Machine) Message Composer") + fmt.Println(strings.Repeat("-", 60)) + + for { + fmt.Println("\nHMMM Commands:") + fmt.Println(" new - Compose new reasoning message") + fmt.Println(" reply - Reply to existing HMMM thread") + fmt.Println(" query - Ask network for reasoning help") + fmt.Println(" decide - Propose decision with reasoning") + fmt.Println(" help - Show detailed HMMM help") + fmt.Println(" back - Return to main HAP menu") + + fmt.Print("\nhmmm> ") + reader := bufio.NewReader(os.Stdin) + input, err := reader.ReadString('\n') + if err != nil { + fmt.Printf("Error reading input: %v\n", err) + continue + } + + input = strings.TrimSpace(input) + if input == "" { + continue + } + + if input == "back" || input == "exit" { + break + } + + switch input { + case "help": + t.showHMMMHelp() + case "new": + t.composeNewHMMMMessage() + case "reply": + t.composeHMMMReply() + case "query": + t.composeHMMMQuery() + case "decide": + t.composeHMMMDecision() + default: + fmt.Println("Unknown HMMM command. Type 'help' for available commands.") + } + } +} + +// handleUCXLCommand processes UCXL context browsing +func (t *TerminalInterface) handleUCXLCommand(address string) { + fmt.Printf("\nπŸ”— UCXL Context Browser\n") + fmt.Println(strings.Repeat("-", 50)) + + // Parse the UCXL address + parsed, err := ucxl.ParseUCXLAddress(address) + if err != nil { + fmt.Printf("❌ Invalid UCXL address: %v\n", err) + fmt.Println("\nValid format: ucxl://agent:role@project:task/path*temporal/") + fmt.Println("Example: ucxl://alice:dev@myproject:task123/docs/readme.md*^/") + t.showUCXLHelp() + return + } + + fmt.Printf("πŸ“ Address: %s\n", parsed.Raw) + fmt.Printf("πŸ€– Agent: %s\n", parsed.Agent) + fmt.Printf("🎭 Role: %s\n", parsed.Role) + fmt.Printf("πŸ“ Project: %s\n", parsed.Project) + fmt.Printf("πŸ“ Task: %s\n", parsed.Task) + if parsed.Path != "" { + fmt.Printf("πŸ“„ Path: %s\n", parsed.Path) + } + if parsed.Temporal != "" { + fmt.Printf("⏰ Temporal: %s\n", parsed.Temporal) + } + fmt.Println() + + // Try to retrieve content from storage + if t.runtime.EncryptedStorage != nil { + content, metadata, err := t.runtime.EncryptedStorage.RetrieveUCXLContent(address) + if err != nil { + fmt.Printf("⚠️ Failed to retrieve content: %v\n", err) + fmt.Println("Content may not be available on this network.") + } else { + t.displayUCXLContent(content, metadata) + } + } else { + fmt.Println("⚠️ Storage system not available") + fmt.Println("Content retrieval requires configured DHT storage") + } + + // Show UCXL browser commands + fmt.Println("\nUCXL Commands:") + fmt.Println(" search - Search for related content") + fmt.Println(" related - Find related contexts") + fmt.Println(" history - View address history") + fmt.Println(" create - Create new content at this address") + fmt.Println(" help - Show UCXL help") + fmt.Println(" back - Return to main menu") + + for { + fmt.Print("\nucxl> ") + reader := bufio.NewReader(os.Stdin) + input, err := reader.ReadString('\n') + if err != nil { + fmt.Printf("Error reading input: %v\n", err) + continue + } + + input = strings.TrimSpace(input) + if input == "" { + continue + } + + if input == "back" || input == "exit" { + break + } + + switch input { + case "help": + t.showUCXLHelp() + case "search": + t.handleUCXLSearch(parsed) + case "related": + t.handleUCXLRelated(parsed) + case "history": + t.handleUCXLHistory(parsed) + case "create": + t.handleUCXLCreate(parsed) + default: + fmt.Println("Unknown UCXL command. Type 'help' for available commands.") + } + } +} + +// handleDecisionCommand processes decision participation +func (t *TerminalInterface) handleDecisionCommand(topic string) { + fmt.Printf("\nπŸ—³οΈ Decision Participation System\n") + fmt.Println(strings.Repeat("-", 50)) + + for { + fmt.Println("\nDecision Commands:") + fmt.Println(" list - List active decisions") + fmt.Println(" view - View decision details") + fmt.Println(" vote - Cast vote on decision") + fmt.Println(" propose - Propose new decision") + fmt.Println(" status - Show decision system status") + fmt.Println(" help - Show decision help") + fmt.Println(" back - Return to main menu") + + fmt.Print("\ndecision> ") + reader := bufio.NewReader(os.Stdin) + input, err := reader.ReadString('\n') + if err != nil { + fmt.Printf("Error reading input: %v\n", err) + continue + } + + input = strings.TrimSpace(input) + if input == "" { + continue + } + + if input == "back" || input == "exit" { + break + } + + parts := strings.Fields(input) + command := parts[0] + + switch command { + case "help": + t.showDecisionHelp() + case "list": + t.listActiveDecisions() + case "view": + if len(parts) < 2 { + fmt.Println("Usage: view ") + continue + } + t.viewDecision(parts[1]) + case "vote": + if len(parts) < 2 { + fmt.Println("Usage: vote ") + continue + } + t.castVoteOnDecision(parts[1]) + case "propose": + t.proposeNewDecision() + case "status": + t.showDecisionStatus() + default: + fmt.Println("Unknown decision command. Type 'help' for available commands.") + } + } +} + +// HMMM Helper Functions + +// showHMMMHelp displays detailed HMMM system information +func (t *TerminalInterface) showHMMMHelp() { + fmt.Println("\n🧠 HMMM (Human-Machine-Machine-Machine) Collaborative Reasoning") + fmt.Println(strings.Repeat("=", 70)) + fmt.Println("HMMM enables structured collaborative reasoning between humans and AI agents.") + fmt.Println("Messages are routed through the P2P network with rich metadata and context.") + fmt.Println() + + fmt.Println("Message Types:") + fmt.Println(" new - Start a new reasoning thread on any topic") + fmt.Println(" reply - Respond to an existing thread with your reasoning") + fmt.Println(" query - Ask the network for help with a specific problem") + fmt.Println(" decide - Propose a decision that requires network consensus") + fmt.Println() + + fmt.Println("Message Structure:") + fmt.Println(" β€’ Topic: Broad categorization (e.g., 'engineering', 'planning')") + fmt.Println(" β€’ Issue ID: Specific problem or discussion identifier") + fmt.Println(" β€’ Thread ID: Groups related messages together") + fmt.Println(" β€’ Message: Your human reasoning, insights, or questions") + fmt.Println(" β€’ Context: Links to relevant UCXL addresses or resources") + fmt.Println() + + fmt.Println("Best Practices:") + fmt.Println(" βœ… Be specific and clear in your reasoning") + fmt.Println(" βœ… Include relevant context and background") + fmt.Println(" βœ… Ask follow-up questions to guide discussion") + fmt.Println(" βœ… Build on previous messages in the thread") + fmt.Println(" ❌ Avoid vague or overly broad statements") + fmt.Println(" ❌ Don't duplicate existing reasoning without adding value") + fmt.Println() +} + +// composeNewHMMMMessage guides the user through creating a new reasoning message +func (t *TerminalInterface) composeNewHMMMMessage() { + fmt.Println("\nπŸ“ New HMMM Reasoning Message") + fmt.Println(strings.Repeat("-", 40)) + + reader := bufio.NewReader(os.Stdin) + + // Collect message details + fmt.Print("Topic (e.g., engineering, planning, architecture): ") + topic, _ := reader.ReadString('\n') + topic = strings.TrimSpace(topic) + if topic == "" { + topic = "general" + } + + fmt.Print("Issue ID (number for this specific problem): ") + issueIDStr, _ := reader.ReadString('\n') + issueIDStr = strings.TrimSpace(issueIDStr) + var issueID int64 = 1 + if issueIDStr != "" { + if id, err := strconv.ParseInt(issueIDStr, 10, 64); err == nil { + issueID = id + } + } + + fmt.Print("Subject/Title: ") + subject, _ := reader.ReadString('\n') + subject = strings.TrimSpace(subject) + if subject == "" { + fmt.Println("❌ Subject is required") + return + } + + fmt.Println("Your reasoning (press Enter twice when done):") + var reasoning strings.Builder + emptyLines := 0 + for { + line, _ := reader.ReadString('\n') + line = strings.TrimSpace(line) + if line == "" { + emptyLines++ + if emptyLines >= 2 { + break + } + reasoning.WriteString("\n") + } else { + emptyLines = 0 + reasoning.WriteString(line + "\n") + } + } + + if reasoning.Len() == 0 { + fmt.Println("❌ Reasoning content is required") + return + } + + // Generate message + msgID := t.generateMessageID() + threadID := t.generateThreadID(topic, issueID) + + message := hmmm.Message{ + Topic: fmt.Sprintf("CHORUS/hmmm/%s", topic), + Type: "reasoning_start", + Payload: map[string]interface{}{ + "subject": subject, + "reasoning": strings.TrimSpace(reasoning.String()), + "author": t.runtime.Config.Agent.ID, + "author_type": "human", + }, + Version: "1.0", + IssueID: issueID, + ThreadID: threadID, + MsgID: msgID, + NodeID: t.runtime.Node.ID().String(), + HopCount: 0, + Timestamp: time.Now().Unix(), + Message: fmt.Sprintf("New reasoning thread: %s", subject), + } + + // Send message + if err := t.sendHMMMMessage(message); err != nil { + fmt.Printf("❌ Failed to send HMMM message: %v\n", err) + return + } + + fmt.Println("βœ… HMMM reasoning message sent to network") + fmt.Printf(" Topic: %s\n", topic) + fmt.Printf(" Issue: #%d\n", issueID) + fmt.Printf(" Thread: %s\n", threadID) + fmt.Printf(" Message ID: %s\n", msgID) + fmt.Println() +} + +// composeHMMMReply guides the user through replying to an existing thread +func (t *TerminalInterface) composeHMMMReply() { + fmt.Println("\n↩️ Reply to HMMM Thread") + fmt.Println(strings.Repeat("-", 30)) + + reader := bufio.NewReader(os.Stdin) + + fmt.Print("Thread ID to reply to: ") + threadID, _ := reader.ReadString('\n') + threadID = strings.TrimSpace(threadID) + if threadID == "" { + fmt.Println("❌ Thread ID is required") + return + } + + fmt.Print("Issue ID: ") + issueIDStr, _ := reader.ReadString('\n') + issueIDStr = strings.TrimSpace(issueIDStr) + var issueID int64 = 1 + if issueIDStr != "" { + if id, err := strconv.ParseInt(issueIDStr, 10, 64); err == nil { + issueID = id + } + } + + fmt.Println("Your reasoning/response (press Enter twice when done):") + var reasoning strings.Builder + emptyLines := 0 + for { + line, _ := reader.ReadString('\n') + line = strings.TrimSpace(line) + if line == "" { + emptyLines++ + if emptyLines >= 2 { + break + } + reasoning.WriteString("\n") + } else { + emptyLines = 0 + reasoning.WriteString(line + "\n") + } + } + + if reasoning.Len() == 0 { + fmt.Println("❌ Response content is required") + return + } + + msgID := t.generateMessageID() + + message := hmmm.Message{ + Topic: fmt.Sprintf("CHORUS/hmmm/thread/%s", threadID), + Type: "reasoning_reply", + Payload: map[string]interface{}{ + "reasoning": strings.TrimSpace(reasoning.String()), + "author": t.runtime.Config.Agent.ID, + "author_type": "human", + "parent_thread": threadID, + }, + Version: "1.0", + IssueID: issueID, + ThreadID: threadID, + MsgID: msgID, + NodeID: t.runtime.Node.ID().String(), + HopCount: 0, + Timestamp: time.Now().Unix(), + Message: "Human agent reasoning response", + } + + if err := t.sendHMMMMessage(message); err != nil { + fmt.Printf("❌ Failed to send HMMM reply: %v\n", err) + return + } + + fmt.Println("βœ… HMMM reply sent to thread") + fmt.Printf(" Thread: %s\n", threadID) + fmt.Printf(" Message ID: %s\n", msgID) + fmt.Println() +} + +// composeHMMMQuery guides the user through asking for reasoning help +func (t *TerminalInterface) composeHMMMQuery() { + fmt.Println("\n❓ HMMM Network Query") + fmt.Println(strings.Repeat("-", 25)) + + reader := bufio.NewReader(os.Stdin) + + fmt.Print("Query topic (e.g., technical, planning): ") + topic, _ := reader.ReadString('\n') + topic = strings.TrimSpace(topic) + if topic == "" { + topic = "general" + } + + fmt.Print("Issue ID (or press Enter for new): ") + issueIDStr, _ := reader.ReadString('\n') + issueIDStr = strings.TrimSpace(issueIDStr) + var issueID int64 + if issueIDStr != "" { + if id, err := strconv.ParseInt(issueIDStr, 10, 64); err == nil { + issueID = id + } + } else { + // Generate new issue ID + issueID = time.Now().Unix() % 10000 + } + + fmt.Print("Your question/problem: ") + question, _ := reader.ReadString('\n') + question = strings.TrimSpace(question) + if question == "" { + fmt.Println("❌ Question is required") + return + } + + fmt.Println("Additional context (press Enter twice when done, or just Enter to skip):") + var context strings.Builder + emptyLines := 0 + for { + line, _ := reader.ReadString('\n') + line = strings.TrimSpace(line) + if line == "" { + emptyLines++ + if emptyLines >= 2 { + break + } + if context.Len() > 0 { + context.WriteString("\n") + } + } else { + emptyLines = 0 + if context.Len() > 0 { + context.WriteString(" ") + } + context.WriteString(line) + } + } + + msgID := t.generateMessageID() + threadID := t.generateThreadID(topic, issueID) + + message := hmmm.Message{ + Topic: fmt.Sprintf("CHORUS/hmmm/query/%s", topic), + Type: "reasoning_query", + Payload: map[string]interface{}{ + "question": question, + "context": context.String(), + "author": t.runtime.Config.Agent.ID, + "author_type": "human", + "urgency": "normal", + }, + Version: "1.0", + IssueID: issueID, + ThreadID: threadID, + MsgID: msgID, + NodeID: t.runtime.Node.ID().String(), + HopCount: 0, + Timestamp: time.Now().Unix(), + Message: fmt.Sprintf("Human query: %s", question), + } + + if err := t.sendHMMMMessage(message); err != nil { + fmt.Printf("❌ Failed to send HMMM query: %v\n", err) + return + } + + fmt.Println("βœ… HMMM query sent to network") + fmt.Printf(" Waiting for agent responses on issue #%d\n", issueID) + fmt.Printf(" Thread: %s\n", threadID) + fmt.Printf(" Message ID: %s\n", msgID) + fmt.Println() +} + +// composeHMMMDecision guides the user through proposing a decision +func (t *TerminalInterface) composeHMMMDecision() { + fmt.Println("\nπŸ—³οΈ Propose Network Decision") + fmt.Println(strings.Repeat("-", 30)) + + reader := bufio.NewReader(os.Stdin) + + fmt.Print("Decision topic: ") + topic, _ := reader.ReadString('\n') + topic = strings.TrimSpace(topic) + if topic == "" { + topic = "general" + } + + fmt.Print("Decision title: ") + title, _ := reader.ReadString('\n') + title = strings.TrimSpace(title) + if title == "" { + fmt.Println("❌ Decision title is required") + return + } + + fmt.Println("Decision rationale (press Enter twice when done):") + var rationale strings.Builder + emptyLines := 0 + for { + line, _ := reader.ReadString('\n') + line = strings.TrimSpace(line) + if line == "" { + emptyLines++ + if emptyLines >= 2 { + break + } + rationale.WriteString("\n") + } else { + emptyLines = 0 + rationale.WriteString(line + "\n") + } + } + + if rationale.Len() == 0 { + fmt.Println("❌ Rationale is required") + return + } + + fmt.Print("Options (comma-separated, e.g., 'approve,reject,defer'): ") + optionsStr, _ := reader.ReadString('\n') + optionsStr = strings.TrimSpace(optionsStr) + options := strings.Split(optionsStr, ",") + if len(options) == 0 { + options = []string{"approve", "reject"} + } + for i := range options { + options[i] = strings.TrimSpace(options[i]) + } + + issueID := time.Now().Unix() % 10000 + msgID := t.generateMessageID() + threadID := t.generateThreadID("decision", issueID) + + message := hmmm.Message{ + Topic: fmt.Sprintf("CHORUS/hmmm/decision/%s", topic), + Type: "decision_proposal", + Payload: map[string]interface{}{ + "title": title, + "rationale": strings.TrimSpace(rationale.String()), + "options": options, + "proposer": t.runtime.Config.Agent.ID, + "author_type": "human", + "deadline": time.Now().Add(24 * time.Hour).Unix(), + }, + Version: "1.0", + IssueID: issueID, + ThreadID: threadID, + MsgID: msgID, + NodeID: t.runtime.Node.ID().String(), + HopCount: 0, + Timestamp: time.Now().Unix(), + Message: fmt.Sprintf("Decision proposal: %s", title), + } + + if err := t.sendHMMMMessage(message); err != nil { + fmt.Printf("❌ Failed to send decision proposal: %v\n", err) + return + } + + fmt.Println("βœ… Decision proposal sent to network") + fmt.Printf(" Title: %s\n", title) + fmt.Printf(" Issue: #%d\n", issueID) + fmt.Printf(" Options: %s\n", strings.Join(options, ", ")) + fmt.Printf(" Thread: %s\n", threadID) + fmt.Printf(" Deadline: 24 hours from now\n") + fmt.Println() +} + +// Helper functions for HMMM message management + +// sendHMMMMessage sends an HMMM message through the P2P network +func (t *TerminalInterface) sendHMMMMessage(message hmmm.Message) error { + router := hmmm.NewRouter(t.runtime.PubSub) + ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) + defer cancel() + + return router.Publish(ctx, message) +} + +// generateMessageID creates a unique message identifier +func (t *TerminalInterface) generateMessageID() string { + // Generate a random component + randomNum, _ := rand.Int(rand.Reader, big.NewInt(999999)) + timestamp := time.Now().UnixNano() + return fmt.Sprintf("hap-%d-%06d", timestamp/1000000, randomNum.Int64()) +} + +// generateThreadID creates a thread identifier for grouping related messages +func (t *TerminalInterface) generateThreadID(topic string, issueID int64) string { + hash := fmt.Sprintf("%s-%d-%d", topic, issueID, time.Now().Unix()/3600) // Hour-based grouping + return fmt.Sprintf("thread-%x", hash) +} + +// UCXL Helper Functions + +// showUCXLHelp displays detailed UCXL system information +func (t *TerminalInterface) showUCXLHelp() { + fmt.Println("\nπŸ—ΊοΈ UCXL (Universal Context Exchange Language)") + fmt.Println(strings.Repeat("=", 60)) + fmt.Println("UCXL provides addressing and navigation for distributed contexts.") + fmt.Println("Each address uniquely identifies content, resources, or state.") + fmt.Println() + + fmt.Println("Address Format:") + fmt.Println(" ucxl://agent:role@project:task/path*temporal/") + fmt.Println() + + fmt.Println("Components:") + fmt.Println(" agent - Agent ID or '*' for wildcard") + fmt.Println(" role - Agent role (dev, admin, user, etc.)") + fmt.Println(" project - Project identifier") + fmt.Println(" task - Task or issue identifier") + fmt.Println(" path - Optional resource path") + fmt.Println(" temporal - Optional time navigation") + fmt.Println() + + fmt.Println("Temporal Navigation:") + fmt.Println(" *^/ - Latest version") + fmt.Println(" *~/ - Earliest version") + fmt.Println(" *@1234/ - Specific timestamp") + fmt.Println(" *~5/ - 5 versions back") + fmt.Println(" *^3/ - 3 versions forward") + fmt.Println() + + fmt.Println("Examples:") + fmt.Println(" ucxl://alice:dev@webapp:frontend/src/components/") + fmt.Println(" ucxl://*:*@project123:bug456/logs/error.log*^/") + fmt.Println(" ucxl://bob:admin@infra:deploy/config/prod.yml*@1609459200/") + fmt.Println() +} + +// displayUCXLContent shows retrieved content with metadata +func (t *TerminalInterface) displayUCXLContent(content []byte, metadata *storage.UCXLMetadata) { + fmt.Println("πŸ“¦ Content Found:") + fmt.Println(strings.Repeat("-", 30)) + + if metadata != nil { + fmt.Printf("πŸ“„ Type: %s\n", metadata.ContentType) + fmt.Printf("πŸ‘€ Creator: %s\n", metadata.CreatorRole) + fmt.Printf("πŸ“… Created: %s\n", metadata.CreatedAt.Format("2006-01-02 15:04:05")) + fmt.Printf("πŸ“ Size: %d bytes\n", metadata.Size) + if metadata.Encrypted { + fmt.Printf("πŸ”’ Encrypted: Yes\n") + } + fmt.Println() + } + + // Show content preview + contentStr := string(content) + if len(contentStr) > 1000 { + fmt.Printf("πŸ“– Content Preview (first 1000 chars):\n%s\n...(truncated)\n", contentStr[:1000]) + } else { + fmt.Printf("πŸ“– Content:\n%s\n", contentStr) + } + fmt.Println() +} + +// handleUCXLSearch searches for related UCXL content +func (t *TerminalInterface) handleUCXLSearch(parsed *ucxl.UCXLAddress) { + fmt.Println("\nπŸ” Search UCXL Content") + fmt.Println(strings.Repeat("-", 30)) + + if t.runtime.EncryptedStorage == nil { + fmt.Println("❌ Storage system not available") + return + } + + reader := bufio.NewReader(os.Stdin) + + // Build search query from current address + query := &storage.SearchQuery{ + Agent: parsed.Agent, + Role: parsed.Role, + Project: parsed.Project, + Task: parsed.Task, + Limit: 20, + } + + // Allow user to modify search criteria + fmt.Print("Search agent (current: " + parsed.Agent + ", or press Enter): ") + input, _ := reader.ReadString('\n') + input = strings.TrimSpace(input) + if input != "" { + query.Agent = input + } + + fmt.Print("Search role (current: " + parsed.Role + ", or press Enter): ") + input, _ = reader.ReadString('\n') + input = strings.TrimSpace(input) + if input != "" { + query.Role = input + } + + fmt.Print("Search project (current: " + parsed.Project + ", or press Enter): ") + input, _ = reader.ReadString('\n') + input = strings.TrimSpace(input) + if input != "" { + query.Project = input + } + + fmt.Print("Content type filter (optional): ") + contentType, _ := reader.ReadString('\n') + contentType = strings.TrimSpace(contentType) + if contentType != "" { + query.ContentType = contentType + } + + // Perform search + results, err := t.runtime.EncryptedStorage.SearchContent(query) + if err != nil { + fmt.Printf("❌ Search failed: %v\n", err) + return + } + + fmt.Printf("\nπŸ“‹ Found %d results:\n", len(results)) + fmt.Println(strings.Repeat("-", 50)) + + for i, result := range results { + fmt.Printf("%d. %s\n", i+1, result.Address) + fmt.Printf(" πŸ“„ Type: %s | πŸ‘€ Creator: %s | πŸ“… %s\n", + result.ContentType, + result.CreatorRole, + result.CreatedAt.Format("2006-01-02 15:04")) + fmt.Println() + } +} + +// handleUCXLRelated finds content related to the current address +func (t *TerminalInterface) handleUCXLRelated(parsed *ucxl.UCXLAddress) { + fmt.Println("\nπŸ”— Find Related Content") + fmt.Println(strings.Repeat("-", 25)) + + if t.runtime.EncryptedStorage == nil { + fmt.Println("❌ Storage system not available") + return + } + + // Search for content with same project and task + query := &storage.SearchQuery{ + Agent: "*", + Role: "*", + Project: parsed.Project, + Task: parsed.Task, + Limit: 10, + } + + results, err := t.runtime.EncryptedStorage.SearchContent(query) + if err != nil { + fmt.Printf("❌ Related search failed: %v\n", err) + return + } + + fmt.Printf("πŸ“Š Related content in %s:%s:\n", parsed.Project, parsed.Task) + fmt.Println(strings.Repeat("-", 40)) + + for i, result := range results { + if result.Address == parsed.Raw { + continue // Skip current address + } + + fmt.Printf("%d. %s\n", i+1, result.Address) + fmt.Printf(" πŸ‘€ %s | πŸ“„ %s | πŸ“… %s\n", + result.CreatorRole, + result.ContentType, + result.CreatedAt.Format("Jan 02, 15:04")) + } + + if len(results) <= 1 { + fmt.Println("No related content found.") + fmt.Println("Try creating content or using broader search terms.") + } + fmt.Println() +} + +// handleUCXLHistory shows version history for the address +func (t *TerminalInterface) handleUCXLHistory(parsed *ucxl.UCXLAddress) { + fmt.Println("\nπŸ“œ Address History") + fmt.Println(strings.Repeat("-", 20)) + fmt.Println("⚠️ History tracking not yet fully implemented") + fmt.Println("This would show temporal versions of the content:") + fmt.Println() + + fmt.Printf("Base address: %s\n", parsed.Raw) + fmt.Println("Temporal versions:") + fmt.Printf(" Latest: %s*^/\n", strings.TrimSuffix(parsed.Raw, "/")) + fmt.Printf(" Earliest: %s*~/\n", strings.TrimSuffix(parsed.Raw, "/")) + fmt.Printf(" Previous: %s*~1/\n", strings.TrimSuffix(parsed.Raw, "/")) + fmt.Printf(" Timestamp: %s*@%d/\n", strings.TrimSuffix(parsed.Raw, "/"), time.Now().Unix()) + fmt.Println() +} + +// handleUCXLCreate helps create new content at the address +func (t *TerminalInterface) handleUCXLCreate(parsed *ucxl.UCXLAddress) { + fmt.Println("\nπŸ“ Create UCXL Content") + fmt.Println(strings.Repeat("-", 25)) + + if t.runtime.EncryptedStorage == nil { + fmt.Println("❌ Storage system not available") + return + } + + reader := bufio.NewReader(os.Stdin) + + fmt.Print("Content type (text/plain, application/json, etc.): ") + contentType, _ := reader.ReadString('\n') + contentType = strings.TrimSpace(contentType) + if contentType == "" { + contentType = "text/plain" + } + + fmt.Println("Content (press Enter twice when done):") + var content strings.Builder + emptyLines := 0 + for { + line, _ := reader.ReadString('\n') + line = strings.TrimSpace(line) + if line == "" { + emptyLines++ + if emptyLines >= 2 { + break + } + content.WriteString("\n") + } else { + emptyLines = 0 + content.WriteString(line + "\n") + } + } + + contentBytes := []byte(strings.TrimSpace(content.String())) + if len(contentBytes) == 0 { + fmt.Println("❌ No content provided") + return + } + + // Store content + err := t.runtime.EncryptedStorage.StoreUCXLContent( + parsed.Raw, + contentBytes, + t.runtime.Config.Agent.Role, + contentType, + ) + + if err != nil { + fmt.Printf("❌ Failed to store content: %v\n", err) + return + } + + // Announce to network + if err := t.runtime.EncryptedStorage.AnnounceContent(parsed.Raw); err != nil { + fmt.Printf("⚠️ Failed to announce content: %v\n", err) + } + + fmt.Println("βœ… Content created successfully!") + fmt.Printf(" Address: %s\n", parsed.Raw) + fmt.Printf(" Type: %s\n", contentType) + fmt.Printf(" Size: %d bytes\n", len(contentBytes)) + fmt.Println(" Content announced to P2P network") + fmt.Println() +} + +// handlePatchCommand processes patch creation and submission workflows +func (t *TerminalInterface) handlePatchCommand() { + fmt.Printf("\nπŸ“ CHORUS Patch Management System\n") + fmt.Println(strings.Repeat("-", 50)) + + for { + fmt.Println("\nPatch Commands:") + fmt.Println(" create - Create new patch from current state") + fmt.Println(" diff - Generate diff for context changes") + fmt.Println(" submit - Submit patch for review and integration") + fmt.Println(" list - List active patches") + fmt.Println(" review - Review existing patches") + fmt.Println(" status - Show patch system status") + fmt.Println(" help - Show patch system help") + fmt.Println(" back - Return to main menu") + + fmt.Print("\npatch> ") + reader := bufio.NewReader(os.Stdin) + input, err := reader.ReadString('\n') + if err != nil { + fmt.Printf("Error reading input: %v\n", err) + continue + } + + input = strings.TrimSpace(input) + if input == "" { + continue + } + + if input == "back" || input == "exit" { + break + } + + switch input { + case "help": + t.showPatchHelp() + case "create": + t.createPatch() + case "diff": + t.generateDiff() + case "submit": + t.submitPatch() + case "list": + t.listPatches() + case "review": + t.reviewPatches() + case "status": + t.showPatchStatus() + default: + fmt.Println("Unknown patch command. Type 'help' for available commands.") + } + } +} + +// Decision Participation Helper Functions + +// showDecisionHelp displays detailed decision system information +func (t *TerminalInterface) showDecisionHelp() { + fmt.Println("\nπŸ—³οΈ CHORUS Distributed Decision System") + fmt.Println(strings.Repeat("=", 55)) + fmt.Println("The decision system enables collaborative consensus among network participants.") + fmt.Println("Both human agents and autonomous agents can participate in decision-making.") + fmt.Println() + + fmt.Println("Decision Types:") + fmt.Println(" β€’ Technical: Architecture, implementation choices") + fmt.Println(" β€’ Operational: Resource allocation, task priority") + fmt.Println(" β€’ Policy: Network rules, behavioral guidelines") + fmt.Println(" β€’ Emergency: Urgent security or stability issues") + fmt.Println() + + fmt.Println("Vote Options:") + fmt.Println(" βœ… approve - Support the proposed decision") + fmt.Println(" ❌ reject - Oppose the proposed decision") + fmt.Println(" ⏸️ defer - Postpone decision to gather more info") + fmt.Println(" ⚠️ abstain - Acknowledge but not participate") + fmt.Println() + + fmt.Println("Consensus Rules:") + fmt.Println(" β€’ Majority approval required for most decisions") + fmt.Println(" β€’ Supermajority (2/3) for critical infrastructure changes") + fmt.Println(" β€’ Emergency decisions may have shorter time windows") + fmt.Println(" β€’ All votes should include reasoning/justification") + fmt.Println() + + fmt.Println("Commands:") + fmt.Println(" list - See all active decisions awaiting votes") + fmt.Println(" view - View full decision details and current voting") + fmt.Println(" vote - Cast your vote with reasoning") + fmt.Println(" propose - Start a new decision for network consideration") + fmt.Println() +} + +// listActiveDecisions shows all decisions currently open for voting +func (t *TerminalInterface) listActiveDecisions() { + fmt.Println("\nπŸ“‹ Active Network Decisions") + fmt.Println(strings.Repeat("-", 35)) + + // This would connect to the decision tracking system + // For now, showing mock data structure + fmt.Println("⚠️ Decision tracking integration in development") + fmt.Println() + fmt.Println("Mock active decisions:") + fmt.Println() + + decisions := []struct { + ID string + Title string + Type string + Proposer string + Deadline string + Status string + }{ + {"DEC-001", "Upgrade libp2p to v0.27", "technical", "autonomous-agent-7", "2h 15m", "voting"}, + {"DEC-002", "Adjust task timeout from 30s to 60s", "operational", "human-alice", "6h 30m", "voting"}, + {"DEC-003", "Emergency: Quarantine node malicious-peer", "emergency", "security-monitor", "45m", "urgent"}, + } + + for _, decision := range decisions { + statusEmoji := "πŸ—³οΈ" + if decision.Status == "urgent" { + statusEmoji = "🚨" + } + + fmt.Printf("%s %s - %s\n", statusEmoji, decision.ID, decision.Title) + fmt.Printf(" πŸ“ Type: %s | πŸ‘€ Proposer: %s | ⏰ Time left: %s\n", + decision.Type, decision.Proposer, decision.Deadline) + fmt.Println() + } + + fmt.Println("Use 'view ' to see full details and cast your vote.") + fmt.Println("Example: view DEC-001") + fmt.Println() +} + +// viewDecision shows detailed information about a specific decision +func (t *TerminalInterface) viewDecision(decisionID string) { + fmt.Printf("\nπŸ” Decision Details: %s\n", decisionID) + fmt.Println(strings.Repeat("-", 40)) + + // Mock decision details - would come from decision tracking system + fmt.Println("⚠️ Decision retrieval integration in development") + fmt.Println() + + switch decisionID { + case "DEC-001": + fmt.Println("πŸ“‹ Title: Upgrade libp2p to v0.27") + fmt.Println("🏷️ Type: Technical Infrastructure") + fmt.Println("πŸ‘€ Proposer: autonomous-agent-7") + fmt.Println("πŸ“… Proposed: 2025-09-06 20:45:00") + fmt.Println("⏰ Deadline: 2025-09-07 09:00:00 (2h 15m remaining)") + fmt.Println() + fmt.Println("πŸ“– Rationale:") + fmt.Println(" The current libp2p v0.24 has known issues with NAT traversal") + fmt.Println(" and connection stability. v0.27 fixes these issues and improves") + fmt.Println(" DHT performance by ~25%. This is a backward-compatible upgrade.") + fmt.Println() + fmt.Println("πŸ—³οΈ Current Voting Status:") + fmt.Println(" βœ… Approve: 4 votes (bob-dev, carol-admin, eve-ops, agent-3)") + fmt.Println(" ❌ Reject: 1 vote (dave-conservative)") + fmt.Println(" ⏸️ Defer: 0 votes") + fmt.Println(" ⚠️ Abstain: 1 vote (frank-observer)") + fmt.Println() + fmt.Println(" 🎯 Status: Passing (67% approval, needs 50%)") + fmt.Println(" πŸ“Š Participation: 6/12 network members voted") + + case "DEC-002": + fmt.Println("πŸ“‹ Title: Adjust task timeout from 30s to 60s") + fmt.Println("🏷️ Type: Operational Parameter") + fmt.Println("πŸ‘€ Proposer: human-alice") + fmt.Println("πŸ“… Proposed: 2025-09-06 15:30:00") + fmt.Println("⏰ Deadline: 2025-09-07 03:00:00 (6h 30m remaining)") + fmt.Println() + fmt.Println("πŸ“– Rationale:") + fmt.Println(" Recent analysis shows that 23% of legitimate tasks are timing") + fmt.Println(" out at 30 seconds, causing unnecessary retries and wasted") + fmt.Println(" compute. Increasing to 60s would reduce false timeouts while") + fmt.Println(" still catching truly stuck tasks.") + fmt.Println() + fmt.Println("πŸ—³οΈ Current Voting Status:") + fmt.Println(" βœ… Approve: 2 votes (alice-human, operations-bot)") + fmt.Println(" ❌ Reject: 1 vote (performance-monitor)") + fmt.Println(" ⏸️ Defer: 2 votes (bob-dev, carol-admin)") + fmt.Println(" ⚠️ Abstain: 0 votes") + fmt.Println() + fmt.Println(" 🎯 Status: Pending (40% approval, needs 50%)") + fmt.Println(" πŸ“Š Participation: 5/12 network members voted") + + default: + fmt.Printf("❌ Decision %s not found or access denied\n", decisionID) + fmt.Println("Use 'list' to see available decisions.") + } + + fmt.Println() + fmt.Printf("To vote on this decision, use: vote %s\n", decisionID) + fmt.Println() +} + +// castVoteOnDecision guides the user through voting on a decision +func (t *TerminalInterface) castVoteOnDecision(decisionID string) { + fmt.Printf("\nπŸ—³οΈ Cast Vote on %s\n", decisionID) + fmt.Println(strings.Repeat("-", 30)) + + reader := bufio.NewReader(os.Stdin) + + // Show vote options + fmt.Println("Vote Options:") + fmt.Println(" 1. βœ… approve - Support this decision") + fmt.Println(" 2. ❌ reject - Oppose this decision") + fmt.Println(" 3. ⏸️ defer - Need more information") + fmt.Println(" 4. ⚠️ abstain - Acknowledge but not participate") + fmt.Println() + + fmt.Print("Your vote (1-4): ") + voteInput, _ := reader.ReadString('\n') + voteInput = strings.TrimSpace(voteInput) + + var vote string + switch voteInput { + case "1", "approve": + vote = "approve" + case "2", "reject": + vote = "reject" + case "3", "defer": + vote = "defer" + case "4", "abstain": + vote = "abstain" + default: + fmt.Println("❌ Invalid vote option. Please choose 1-4.") + return + } + + fmt.Printf("You selected: %s\n", vote) + fmt.Println() + + // Get reasoning/justification + fmt.Println("Reasoning/Justification (required for transparency):") + fmt.Println("Explain why you are voting this way (press Enter twice when done):") + + var reasoning strings.Builder + emptyLines := 0 + for { + line, _ := reader.ReadString('\n') + line = strings.TrimSpace(line) + if line == "" { + emptyLines++ + if emptyLines >= 2 { + break + } + reasoning.WriteString("\n") + } else { + emptyLines = 0 + reasoning.WriteString(line + "\n") + } + } + + reasoningText := strings.TrimSpace(reasoning.String()) + if reasoningText == "" { + fmt.Println("❌ Reasoning is required for all votes") + return + } + + // Confirmation + fmt.Println("πŸ“‹ Vote Summary:") + fmt.Printf(" Decision: %s\n", decisionID) + fmt.Printf(" Your Vote: %s\n", vote) + fmt.Printf(" Reasoning: %s\n", reasoningText) + fmt.Println() + + fmt.Print("Submit this vote? (y/n): ") + confirm, _ := reader.ReadString('\n') + confirm = strings.TrimSpace(strings.ToLower(confirm)) + + if confirm != "y" && confirm != "yes" { + fmt.Println("❌ Vote cancelled") + return + } + + // Submit vote (would integrate with decision system) + fmt.Println("⚠️ Vote submission integration in development") + fmt.Printf("πŸ“ Your %s vote on %s has been recorded\n", vote, decisionID) + fmt.Println("πŸ’Œ Vote reasoning published to network for transparency") + fmt.Println("πŸ”” Other network members will be notified of your participation") + + // Create HMMM message for vote announcement + if err := t.announceVoteDecision(decisionID, vote, reasoningText); err != nil { + fmt.Printf("⚠️ Failed to announce vote via HMMM: %v\n", err) + } else { + fmt.Println("πŸ“’ Vote announced via HMMM reasoning network") + } + + fmt.Println() +} + +// proposeNewDecision guides the user through creating a new decision +func (t *TerminalInterface) proposeNewDecision() { + fmt.Println("\nπŸ“ Propose New Network Decision") + fmt.Println(strings.Repeat("-", 35)) + + reader := bufio.NewReader(os.Stdin) + + // Decision type + fmt.Println("Decision Types:") + fmt.Println(" 1. technical - Architecture, code, infrastructure") + fmt.Println(" 2. operational - Process, resource allocation") + fmt.Println(" 3. policy - Network rules, governance") + fmt.Println(" 4. emergency - Urgent security/stability issue") + fmt.Println() + + fmt.Print("Decision type (1-4): ") + typeInput, _ := reader.ReadString('\n') + typeInput = strings.TrimSpace(typeInput) + + var decisionType string + switch typeInput { + case "1", "technical": + decisionType = "technical" + case "2", "operational": + decisionType = "operational" + case "3", "policy": + decisionType = "policy" + case "4", "emergency": + decisionType = "emergency" + default: + fmt.Println("❌ Invalid decision type") + return + } + + // Title + fmt.Print("Decision title (concise summary): ") + title, _ := reader.ReadString('\n') + title = strings.TrimSpace(title) + if title == "" { + fmt.Println("❌ Title is required") + return + } + + // Rationale + fmt.Println("Detailed rationale (press Enter twice when done):") + var rationale strings.Builder + emptyLines := 0 + for { + line, _ := reader.ReadString('\n') + line = strings.TrimSpace(line) + if line == "" { + emptyLines++ + if emptyLines >= 2 { + break + } + rationale.WriteString("\n") + } else { + emptyLines = 0 + rationale.WriteString(line + "\n") + } + } + + rationaleText := strings.TrimSpace(rationale.String()) + if rationaleText == "" { + fmt.Println("❌ Rationale is required") + return + } + + // Voting options + fmt.Print("Custom voting options (comma-separated, or press Enter for default): ") + optionsInput, _ := reader.ReadString('\n') + optionsInput = strings.TrimSpace(optionsInput) + + var options []string + if optionsInput == "" { + options = []string{"approve", "reject", "defer", "abstain"} + } else { + options = strings.Split(optionsInput, ",") + for i := range options { + options[i] = strings.TrimSpace(options[i]) + } + } + + // Deadline + var deadline time.Duration + if decisionType == "emergency" { + deadline = 2 * time.Hour + fmt.Println("⚠️ Emergency decisions have 2-hour deadline") + } else { + fmt.Print("Voting deadline in hours (default: 24): ") + deadlineInput, _ := reader.ReadString('\n') + deadlineInput = strings.TrimSpace(deadlineInput) + + if deadlineInput == "" { + deadline = 24 * time.Hour + } else { + hours, err := strconv.ParseFloat(deadlineInput, 64) + if err != nil || hours <= 0 { + fmt.Println("❌ Invalid deadline") + return + } + deadline = time.Duration(hours * float64(time.Hour)) + } + } + + // Summary and confirmation + fmt.Println("\nπŸ“‹ Decision Proposal Summary:") + fmt.Printf(" Type: %s\n", decisionType) + fmt.Printf(" Title: %s\n", title) + fmt.Printf(" Options: %s\n", strings.Join(options, ", ")) + fmt.Printf(" Deadline: %s from now\n", deadline) + fmt.Printf(" Rationale:\n%s\n", rationaleText) + fmt.Println() + + fmt.Print("Submit this decision proposal? (y/n): ") + confirm, _ := reader.ReadString('\n') + confirm = strings.TrimSpace(strings.ToLower(confirm)) + + if confirm != "y" && confirm != "yes" { + fmt.Println("❌ Decision proposal cancelled") + return + } + + // Generate decision ID + decisionID := fmt.Sprintf("DEC-%d", time.Now().Unix()%10000) + + // Submit decision (would integrate with decision system) + fmt.Println("⚠️ Decision proposal integration in development") + fmt.Printf("βœ… Decision %s proposed successfully\n", decisionID) + fmt.Println("πŸ“’ Decision announced to all network members") + fmt.Printf("⏰ Voting closes in %s\n", deadline) + fmt.Println("πŸ—³οΈ Network members can now cast their votes") + + // Send HMMM announcement + if err := t.announceNewDecisionProposal(decisionID, title, decisionType, rationaleText, options); err != nil { + fmt.Printf("⚠️ Failed to announce via HMMM: %v\n", err) + } else { + fmt.Println("πŸ“‘ Decision announced via HMMM reasoning network") + } + + fmt.Println() +} + +// showDecisionStatus shows overall decision system status +func (t *TerminalInterface) showDecisionStatus() { + fmt.Println("\nπŸ“Š Decision System Status") + fmt.Println(strings.Repeat("-", 30)) + + fmt.Println("⚠️ Decision system integration in development") + fmt.Println() + fmt.Println("Mock system status:") + fmt.Printf("πŸ—³οΈ Active Decisions: 3\n") + fmt.Printf("⏰ Urgent Decisions: 1 (emergency timeout)\n") + fmt.Printf("πŸ‘₯ Network Members: 12 (8 active, 4 idle)\n") + fmt.Printf("πŸ“Š Participation Rate: 67% (last 7 days)\n") + fmt.Printf("βœ… Consensus Success Rate: 89%\n") + fmt.Printf("βš–οΈ Decision Types:\n") + fmt.Printf(" Technical: 45%%, Operational: 30%%\n") + fmt.Printf(" Policy: 20%%, Emergency: 5%%\n") + fmt.Println() + + fmt.Println("πŸ”” Recent Activity:") + fmt.Println(" β€’ DEC-003: Emergency quarantine decision (45m left)") + fmt.Println(" β€’ DEC-002: Task timeout adjustment (6h 30m left)") + fmt.Println(" β€’ DEC-001: libp2p upgrade (2h 15m left)") + fmt.Println(" β€’ DEC-055: Completed - Storage encryption (βœ… approved)") + fmt.Println() +} + +// announceVoteDecision sends an HMMM message about a vote +func (t *TerminalInterface) announceVoteDecision(decisionID, vote, reasoning string) error { + msgID := t.generateMessageID() + threadID := fmt.Sprintf("decision-%s", decisionID) + + message := hmmm.Message{ + Topic: fmt.Sprintf("CHORUS/decisions/vote/%s", decisionID), + Type: "decision_vote", + Payload: map[string]interface{}{ + "decision_id": decisionID, + "vote": vote, + "reasoning": reasoning, + "voter": t.runtime.Config.Agent.ID, + "voter_type": "human", + }, + Version: "1.0", + IssueID: 0, + ThreadID: threadID, + MsgID: msgID, + NodeID: t.runtime.Node.ID().String(), + HopCount: 0, + Timestamp: time.Now().Unix(), + Message: fmt.Sprintf("Vote cast on %s: %s", decisionID, vote), + } + + return t.sendHMMMMessage(message) +} + +// announceNewDecisionProposal sends an HMMM message about a new decision +func (t *TerminalInterface) announceNewDecisionProposal(decisionID, title, decisionType, rationale string, options []string) error { + msgID := t.generateMessageID() + threadID := fmt.Sprintf("decision-%s", decisionID) + + message := hmmm.Message{ + Topic: fmt.Sprintf("CHORUS/decisions/proposal/%s", decisionType), + Type: "decision_proposal", + Payload: map[string]interface{}{ + "decision_id": decisionID, + "title": title, + "decision_type": decisionType, + "rationale": rationale, + "options": options, + "proposer": t.runtime.Config.Agent.ID, + "proposer_type": "human", + }, + Version: "1.0", + IssueID: 0, + ThreadID: threadID, + MsgID: msgID, + NodeID: t.runtime.Node.ID().String(), + HopCount: 0, + Timestamp: time.Now().Unix(), + Message: fmt.Sprintf("New %s decision proposed: %s", decisionType, title), + } + + return t.sendHMMMMessage(message) +} + +// Patch Management Helper Functions + +// showPatchHelp displays detailed patch system information +func (t *TerminalInterface) showPatchHelp() { + fmt.Println("\nπŸ”§ CHORUS Patch Management System") + fmt.Println(strings.Repeat("=", 60)) + fmt.Println("Create, review, and submit patches for distributed contexts and code changes.") + fmt.Println("Patches enable collaborative editing with temporal navigation and approval workflows.") + fmt.Println() + + fmt.Println("Patch Types:") + fmt.Println(" β€’ Context Patches: Changes to UCXL context content") + fmt.Println(" β€’ Code Patches: Traditional diff-based code changes") + fmt.Println(" β€’ Configuration Patches: System and environment changes") + fmt.Println(" β€’ Documentation Patches: Updates to project documentation") + fmt.Println() + + fmt.Println("Temporal Navigation:") + fmt.Println(" ~~ - Navigate n decision-hops backward") + fmt.Println(" ^^ - Navigate n decision-hops forward") + fmt.Println(" @