odek

package module
v1.0.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: May 29, 2026 License: MIT Imports: 15 Imported by: 0

README

odek

Minimal Go autonomous agent runtime — tiny dep tree, small static binary, instant startup.

One binary. One loop. Zero frameworks. ReAct (Reasoning + Acting) — think, therefore act.

# Install
go install github.com/BackendStack21/odek/cmd/odek@latest

# Use (set ODEK_API_KEY, DEEPSEEK_API_KEY, or OPENAI_API_KEY)
export ODEK_API_KEY=sk-...
odek run "How many lines in go.mod?"
# → 3 lines

Why odek

odek is not a framework. It's a runtime — the smallest possible surface area between an LLM and your tools.

odek Python agents (LangChain, CrewAI, etc.)
Dependencies 5. 3× stdlib, 2× 21no.de 200+ packages
Binary size ~11 MB static 50-200 MB with venv
Startup Instant 2-10s (Python imports)
Sandbox --sandbox flag Requires manual Docker setup
Tool interface One interface, one method Class hierarchies + decorators

Strategic Features

🔒 Sandboxed Execution

Every session can run in an isolated Docker container: no network, no host mounts beyond the working directory, zero capabilities, destroyed on exit. odek serve enables the sandbox by default; odek run keeps it opt-in but warns when running unsandboxed. --ctx files are auto-injected into the container at /workspace/. Full security model in docs/SANDBOXING.md.

🛡️ Prompt-Injection-Aware

External content the agent ingests (browser, read_file, shell, search_files, multi_grep, transcribe, MCP tools) is wrapped in per-call nonce'd <untrusted_content> boundaries so the model can distinguish data from instructions. The danger classifier resists 8 known shell-evasion tricks ($(), backticks, $IFS, command/exec, \rm, basenamed absolute paths). Approvers engage friction mode after 3 same-class approvals in 60 s. Memory episodes from tainted sessions are stored but never auto-replayed. Skill auto-save tracks provenance and pins untrusted suggestions for explicit odek skill promote. odek audit <session-id> surfaces every ingest + per-turn divergence heuristic. Full threat model in docs/SECURITY.md.

🧩 Sub-Agent Delegation

Parallel OS-process sub-agents via delegate_tasks. True isolation — each sub-agent is a fresh odek subagent process with its own config, tools, and termination timeout. Up to 8 concurrent workers. docs/SUBAGENTS.md

🧠 Skill System (on by default)

Skill-matched SKILL.md files load on-demand. Auto-learns from patterns every session — detects multi-step procedures, error recoveries, repeated actions, and user corrections. LLM-enhanced: each detected pattern is enriched with an LLM-generated name, description, trigger keywords, and structured body with overview, steps, pitfalls, and verification sections. Use --no-learn to disable. Import skills from any URI with automatic LLM risk assessment. docs/CLI.md#skills

💾 Persistent Memory

Three tiers: facts (agent-managed durable entries), session buffer (auto-appended turn summaries), episodes (LLM-extracted knowledge from past sessions). Merge-on-write via go-vector RandomProjections — cosine >0.7 auto-merges, <0.3 auto-adds. Saves ~80% LLM calls. docs/MEMORY.md

🔧 Multi-Turn Sessions

Save, resume, list, trim, and clean up conversations. Sessions persist as JSON in ~/.odek/sessions/. Continue any session with odek continue. docs/SESSIONS.md

🏗️ Layerable Config

Four-layer priority chain: global (~/.odek/config.json)project (./odek.json)ODEK_* env vars → CLI flags. ${VAR} substitution in config files. docs/CONFIG.md

🔌 LLM-Agnostic

Any OpenAI-compatible endpoint: Deepseek, OpenAI, Anthropic, Ollama, vLLM, Groq, Together, Fireworks — anything that speaks /chat/completions. Per-model profiles for thinking depth and context windows. docs/PROVIDERS.md

🌐 Web UI

odek serve — browser-based agent with @ resource completion (@file.go, @sess:abc123), drag-and-drop file attachments, WebSocket streaming, and a full IDE-style console. docs/WEBUI.md

🤖 Telegram Bot

Run agent tasks directly from Telegram via long-polling. Supports slash commands (/plan, /sessions, /resume, /prune, /help, etc.), voice message transcription, photo analysis, conversation persistence across restarts, saved plan files, and daily token budgeting. No external Telegram libraries — built on stdlib net/http. docs/TELEGRAM.md

📎 File Attachments

Attach files to any prompt with --ctx / -c (CLI), @filename inline references (CLI + REPL + Web UI), or drag-and-drop (Web UI). File content is injected as context blocks before the task — no tool calls needed. Comma-separate multiple files: --ctx main.go,lib.go. docs/CLI.md#file-attachments

🔗 MCP (Two-Way)

Server (odek mcp) — expose odek's native tools (shell, read/write/search files, patch, browser) to Claude Code, Cursor, and any MCP client. Client (mcp_servers config) — odek connects to external MCP servers (Playwright, Fetch, GitHub, SQLite, etc.) and makes their tools available to the agent as <server>__<tool>. Both directions in one binary. docs/MCP.md

🔍 Native Tools

Built-in read_file, write_file, search_files, patch, shell, and browser tools. All gated by a unified security layer (dangerous config) — classify operations as allow / deny / prompt per risk class. No third-party dependencies. docs/SECURITY.md


Quick Start

# Single-shot task
odek run "List the files"

# With session persistence
odek run --session "Refactor auth module"
odek continue "Add rate limiting"

# Sandboxed (Docker isolation)
odek run --sandbox "npm audit"

# Different model
odek run --model gpt-4o --base-url https://api.openai.com/v1 "Explain this"

# With skill learning (on by default — use --no-learn to disable)
odek run "Set up a Go project with CI"

# Interactive REPL
odek repl

# Attach files for context
odek run --ctx data.csv "analyze this"
odek run --ctx main.go,lib.go "compare these files"
odek run "@README.md what does this project do?"

Cheatsheet

Commands
Command What it does
odek run <task> Single-shot task
odek run --session <task> Save conversation as session
odek continue [--id <id>] <task> Resume a saved session
odek repl Interactive multi-turn REPL
odek session list List recent sessions
odek session show [id] View session transcript
odek session delete <id> Delete a session
odek session trim <id> <n> Keep last n messages
odek session cleanup <days> Delete old sessions
odek skill list List available skills
odek skill view <name> View skill content
odek skill delete <name> Delete a skill
odek skill promote <name> Promote a tainted auto-saved skill after review
odek skill import <uri> Import skill from URL
odek skill curate Audit skill quality/overlap
odek audit <session-id> Print the prompt-injection audit log for a session
odek audit --list List sessions with ingest counts and divergence flags
odek serve [--addr :8080] Start Web UI server (sandbox on by default; --no-sandbox to disable)
odek subagent --goal <string> Run a focused sub-task
odek init [--global] Create config file
odek mcp [--sandbox] Start MCP server — expose tools to Claude Code
odek version Print version
Key Flags
Flag What it does
--model <name> LLM model (e.g. deepseek-v4-flash, gpt-4o)
--base-url <url> API endpoint URL
--sandbox Run in Docker sandbox
--thinking <level> Reasoning depth (enabled/disabled/low/medium/high)
--learn Enable skill learning mode — on by default
--no-learn Disable skill learning mode
--system <prompt> Override system prompt
--max-iter <n> Max think→act cycles (default 90)
--prompt-caching Enable Anthropic/OpenAI/DeepSeek prompt caching markers
--no-color Disable colored output
--ctx <files> / -c Attach files as context blocks (comma-separated)
--no-agents Skip AGENTS.md project file

Docs

Doc Covers
CLI Reference All commands, subcommands, flags, error codes
Cheat Sheet CLI quick reference, key flags, config snippets
Configuration Config files, env vars, priority chain, all sections
Programmatic API SDK Guide: import, Agent lifecycle, Tool interface, multi-turn sessions, memory system, model profiles, complete examples
Providers & Models Supported providers, thinking config, context windows
Prompt Caching Anthropic/OpenAI/DeepSeek caching support, config, metrics
Memory Three-tier design, go-vector merge-on-write, memory tool
Sessions Multi-turn conversations, save/resume/trim/cleanup
Telegram Bot Telegram integration: bot client, slash commands, session management, plans, media downloads
Sandboxing Docker isolation model, config, security hardening
Security Threat model, prompt injection defense, sandbox model
Sub-Agents Task decomposition, delegation tool, subagent protocol
Web UI odek serve, WebSocket protocol, @ resource resolution
Self-Learning LLM-enhanced skill learning, pattern detection, auto-curation
Skills Trigger-matched skills, learning, import, curation
MCP Serve tools to Claude Code + connect to external MCP servers
Development Building, testing, contributing, project structure

Programmatic API

import "github.com/BackendStack21/odek"

agent, err := odek.New(odek.Config{
    Model:          "deepseek-v4-flash",
    APIKey:         os.Getenv("ODEK_API_KEY"),
    MaxIterations:  30,
    Tools:          []odek.Tool{&myCustomTool{}},
    SystemMessage:  "You are an expert at refactoring Go code.",
})
defer agent.Close()

result, err := agent.Run(context.Background(), "Refactor this module")

The full Config struct supports: BaseURL, Thinking, SandboxCleanup, Renderer, MemoryConfig, MemoryDir, Skills, SkillManager, and NoProjectFile.


Test

go test ./...                 # full suite, no setup required
go test -race ./...           # also clean under the race detector
go test -cover ./...          # per-package coverage report
ODEK_E2E=1 go test ./cmd/odek/   # opt-in Docker / subprocess E2E suite

Everything runs with go test — no Docker, no network, no external services required for the default unit suite. The opt-in ODEK_E2E=1 set exercises the sandbox, sub-agent subprocess pipeline, and Web UI handshake against real Docker / real processes.


License

MIT

Documentation

Overview

Package odek is a minimal Go agent loop runtime.

odek implements the ReAct (Reasoning + Acting) pattern — the "think, therefore act" loop that powers autonomous AI agents. It is not a framework or an SDK. It is a runtime: one loop, one binary, minimal deps.

Design

  • Minimal external dependencies. stdlib + a few focused packages.
  • Session isolation via Docker containers (--sandbox).
  • LLM-agnostic. Any OpenAI-compatible endpoint works.
  • Tool-first. Tools are the only extension point.

Security

When running with --sandbox, each session executes in a fresh Docker container. The container has no network access, no host mounts beyond the working directory, and is destroyed on exit. The agent can never access files outside its working directory.

Index

Constants

View Source
const ProjectFileName = "AGENTS.md"

ProjectFileName is the name of the project-level instructions file that odek automatically loads from the working directory.

Variables

View Source
var KnownProfiles = []struct {
	Prefix  string
	Profile ModelProfile
}{
	{
		Prefix: "deepseek-v4-pro",
		Profile: ModelProfile{
			Label:           "DeepSeek v4 Pro",
			DefaultThinking: "enabled",
			Timeout:         180,
			MaxContext:      1_000_000,
		},
	},
	{
		Prefix: "deepseek-v4-flash",
		Profile: ModelProfile{
			Label:           "DeepSeek v4 Flash",
			DefaultThinking: "",
			Timeout:         90,
			MaxContext:      131_072,
		},
	},
	{
		Prefix: "deepseek-",
		Profile: ModelProfile{
			Label:      "DeepSeek (generic)",
			MaxContext: 131_072,
		},
	},
}

KnownProfiles lists all built-in model profiles. Each entry is matched by longest prefix — "deepseek-v4-flash" matches before "deepseek-" would. Add new profiles here; the rest of odek consumes them automatically.

Functions

func BuildRuntimeContext

func BuildRuntimeContext(platform string) string

BuildRuntimeContext returns a system prompt header with OS, hostname, working directory, current date/time, and platform-specific formatting rules for the given transport (platform). platform can be "telegram", "terminal", "web", or empty for generic.

This context eliminates the need for the agent to run shell commands to discover its own environment — the most common waste of tokens in CLI agent usage.

func LoadProjectFile

func LoadProjectFile() string

LoadProjectFile reads ProjectFileName from the current working directory. Returns the file content (trimmed) if it exists and is readable. Returns empty string if the file doesn't exist or can't be read. Checks for symlinks to prevent following attacker-controlled paths. The content is intended to be appended to the system message with a clear header — use it for project conventions, architecture notes, etc.

func ProfileLabel

func ProfileLabel(model string) string

ProfileLabel returns the human-readable label for a model, or the model name itself if no profile matches. Used in CLI headers and status output.

Types

type Agent

type Agent struct {
	// contains filtered or unexported fields
}

Agent is the agent loop runtime.

func New

func New(cfg Config) (*Agent, error)

New creates a new Agent with the given configuration.

If Config.SandboxCleanup is set, the cleanup function is called when Close() is invoked. The caller is responsible for creating the sandbox container and wiring up tool executables to use it before calling New().

func (*Agent) Close

func (a *Agent) Close() error

Close cleans up resources. If a sandbox container was created, it is destroyed. Always call Close() when done with the agent.

func (*Agent) Memory

func (a *Agent) Memory() *memory.MemoryManager

Memory returns the agent's memory manager. Used by the CLI layer to append buffer entries after each turn and signal session end. Returns nil if memory is disabled.

func (*Agent) Run

func (a *Agent) Run(ctx context.Context, task string) (string, error)

Run executes the agent loop for the given task and returns the final answer.

func (*Agent) RunWithMessages

func (a *Agent) RunWithMessages(ctx context.Context, messages []llm.Message) (string, []llm.Message, error)

RunWithMessages executes the agent loop starting from a pre-built message history. Use this for multi-turn conversations where the full conversation context (system prompt, prior turns) has been loaded from a session file and the new user message appended.

Returns the final answer plus the complete updated message history. The caller should persist the history (e.g. to a session file) so the conversation can be continued in a future call.

func (*Agent) SkillManager

func (a *Agent) SkillManager() *skills.SkillManager

SkillManager returns the agent's skill manager. Used by the CLI, WebUI, and Telegram layers to run learning heuristics after agent completion. Returns nil if skills are disabled.

func (*Agent) SwitchModel

func (a *Agent) SwitchModel(model string)

SwitchModel updates the LLM model used by this agent at runtime. The model string must be a valid OpenAI-compatible model identifier. This is safe to call between RunWithMessages calls to switch models mid-session. Empty strings are silently ignored.

func (*Agent) SwitchThinking added in v1.0.0

func (a *Agent) SwitchThinking(thinking string)

SwitchThinking updates the reasoning/thinking mode used by this agent at runtime. Accepts the same values as Config.Thinking: "enabled", "disabled", "low", "medium", "high", or "" (provider default / off). Safe to call between RunWithMessages calls to toggle thinking per-query.

func (*Agent) TotalCacheCreationTokens

func (a *Agent) TotalCacheCreationTokens() int

TotalCacheCreationTokens returns the cumulative Anthropic cache creation tokens across all iterations of the most recent run.

func (*Agent) TotalCacheReadTokens

func (a *Agent) TotalCacheReadTokens() int

TotalCacheReadTokens returns the cumulative Anthropic cache read tokens across all iterations of the most recent run.

func (*Agent) TotalCachedTokens

func (a *Agent) TotalCachedTokens() int

TotalCachedTokens returns the cumulative OpenAI cached prompt tokens across all iterations of the most recent run.

func (*Agent) TotalInputTokens

func (a *Agent) TotalInputTokens() int

TotalInputTokens returns the cumulative prompt tokens consumed across all iterations of the most recent RunWithMessages call.

func (*Agent) TotalOutputTokens

func (a *Agent) TotalOutputTokens() int

TotalOutputTokens returns the cumulative completion tokens generated across all iterations of the most recent RunWithMessages call.

type Config

type Config struct {
	// Model is the LLM model identifier (e.g., "deepseek-v4-flash").
	Model string

	// BaseURL is the OpenAI-compatible API endpoint.
	// Default: "https://api.deepseek.com/v1"
	BaseURL string

	// APIKey authenticates with the LLM provider.
	// Falls back to DEEPSEEK_API_KEY, then OPENAI_API_KEY env vars.
	APIKey string

	// Thinking controls the model's reasoning depth. Provider-specific:
	//
	//   Deepseek: "enabled" or "disabled" → {"type": "enabled"}
	//   OpenAI o-series: "low", "medium", "high" → {"reasoning_effort": "low"}
	//
	// When empty, the model's profile default is used. If the profile also
	// has no default, the field is not sent (provider default behavior).
	Thinking string

	// Temperature controls LLM output randomness (0.0–2.0).
	// Negative = omit from request (use provider default).
	// 0.0 = deterministic, 1.0 = creative. Default: 0.0 for benchmark
	// stability; set to -1 to use provider defaults.
	Temperature float64

	// ThinkingBudget is the maximum thinking tokens for Anthropic extended thinking (default 5000).
	ThinkingBudget int

	// Tools available to the agent.
	Tools []Tool

	// MaxIterations caps the number of think→act cycles (default: 90).
	MaxIterations int

	// SystemMessage is the system prompt injected at the start of every run.
	// Runtime context (OS, hostname, cwd, date, platform) is automatically
	// prepended to this message before it reaches the LLM.
	// If AGENTS.md exists in the working directory, its content is appended
	// automatically. Set NoProjectFile to true to skip this.
	SystemMessage string

	// RuntimeContext, when set, prepends environment awareness to the system
	// message: OS, hostname, working directory, current date/time, and
	// platform-specific formatting rules. Each entry point (CLI, Telegram,
	// WebUI) sets this automatically. When empty, BuildRuntimeContext("")
	// provides generic terminal context.
	RuntimeContext string

	// NoProjectFile disables automatic loading of AGENTS.md from the
	// working directory. By default, odek reads AGENTS.md and appends
	// its content to the system message with a "Project Instructions" header.
	NoProjectFile bool

	// SandboxCleanup, if set, is called by Agent.Close() to destroy the
	// Docker sandbox container. Set by the CLI when --sandbox is active.
	// Programmatic API users can set this to their own cleanup logic
	// (e.g., remove a container, delete a VM, tear down a network).
	// When nil, Close() is a no-op.
	SandboxCleanup func() error

	// Renderer, if set, produces colored terminal output for each phase
	// of the agent loop. When nil, the agent runs silently (programmatic API).
	Renderer *render.Renderer

	// ToolEventHandler, if set, is invoked for each tool call and result
	// during the agent loop. Fires "tool_call" before and "tool_result"
	// after each tool invocation. Used by the WebUI for live streaming.
	ToolEventHandler func(event string, name string, data string)

	// InteractionMode controls tool-call rendering: "engaging" (default), "enhance", "verbose", or "off".
	InteractionMode string

	// IterationCallback, if set, is invoked after each iteration of the
	// agent loop with progress info (turn number, tokens, tools called).
	// Used by the Telegram handler for periodic progress updates.
	IterationCallback loop.IterationCallback

	// Skills configures the skill system. When nil, skills are disabled.
	Skills *skills.SkillsConfig

	// SkillManager holds the loaded skill state. Passed by the CLI layer;
	// when nil, New() auto-loads from default directories.
	SkillManager *skills.SkillManager

	// MemoryDir sets the directory for persistent memory storage.
	// Default: ~/.odek/memory/
	MemoryDir string

	// MemoryConfig controls the memory system (facts, buffer, episodes).
	// Default: memory.DefaultMemoryConfig()
	MemoryConfig memory.MemoryConfig

	// PromptCaching enables prompt caching markers for supported providers.
	// When enabled (default: false), the system prompt and first user message
	// are annotated with cache_control markers, and Anthropic-style system
	// blocks are used. Supported by:
	//   - Anthropic (explicit cache_control markers)
	//   - DeepSeek (automatic — prefix stability helps)
	//   - OpenAI (automatic — prefix stability helps)
	//
	// When disabled (default), no cache markers are sent and the system
	// prompt stays in the messages array for maximum provider compatibility.
	// Enable this when using Anthropic models to get ~90% cost reduction
	// on cached tokens and ~60-80% TTFT latency reduction.
	PromptCaching bool

	// MaxToolParallel controls how many tool calls run concurrently per
	// agent iteration. 0 = use default (4). Models that emit multiple
	// parallel tool calls benefit from concurrent execution of I/O-bound
	// tools like read_file, search_files, and web_search.
	MaxToolParallel int

	// SkillEventHandler, if set, is invoked when a skill lifecycle event
	// occurs (loaded, autoloaded, saved, deleted, etc.). Used by WebUI
	// (WebSocket streaming) and Telegram (inline messages).
	SkillEventHandler func(event skills.SkillEvent)

	// Approver gates dangerous tool operations. When set and the LLM returns
	// multiple tool calls in one iteration, a single batch approval prompt
	// is shown instead of N individual prompts. If denied, no tools run
	// for that iteration. If approved, individual tool-level PromptCommand
	// calls are bypassed via SetTrustAll.
	Approver danger.Approver

	// DangerousConfig holds the user's risk class configuration (Allow/Deny/
	// Prompt per risk class). Used by the batch gate to decide whether a
	// tool call needs approval before showing the prompt. When nil, the
	// batch gate plays safe and shows the prompt for any classified tool.
	DangerousConfig *danger.DangerousConfig
}

Config configures an Agent instance.

type ModelProfile

type ModelProfile struct {
	// Label is a human-readable name for the model family.
	Label string

	// DefaultThinking is the thinking value applied when Config.Thinking
	// is empty. Empty string means don't send the field (provider default).
	DefaultThinking string

	// Timeout is the default request timeout in seconds.
	// Zero means use the global default (120s). Increased for
	// models that take longer to reason (e.g. deepseek-v4-pro).
	Timeout int

	// MaxContext is the model's maximum context window in tokens.
	// The loop engine automatically trims conversation history when
	// estimated tokens approach this limit. Zero means no limit
	// enforcement (unknown or effectively unlimited models).
	MaxContext int
}

ModelProfile holds per-model defaults applied when the user hasn't explicitly provided a value. Zero values leave the system default.

func LookupProfile

func LookupProfile(model string) *ModelProfile

LookupProfile returns the best-matching ModelProfile for a model name, or nil if no profile matches. Matching uses longest prefix — a model named "deepseek-v4-flash-custom" would match "deepseek-v4-flash".

type Tool

type Tool interface {
	Name() string
	Description() string
	Schema() any // JSON Schema for the tool's parameters
	Call(args string) (string, error)
}

Tool represents a single capability the agent can invoke.

Directories

Path Synopsis
cmd
odek command
internal
config
Package config loads and merges odek configuration from multiple sources.
Package config loads and merges odek configuration from multiple sources.
danger
Package danger classifies shell commands by risk level and provides a configurable approval system for dangerous operations.
Package danger classifies shell commands by risk level and provides a configurable approval system for dangerous operations.
llm
Package llm provides an OpenAI-compatible HTTP client using only stdlib.
Package llm provides an OpenAI-compatible HTTP client using only stdlib.
loop
Package loop implements the ReAct (Reasoning + Acting) agent loop.
Package loop implements the ReAct (Reasoning + Acting) agent loop.
mcp
Package mcp implements a Model Context Protocol server over stdio.
Package mcp implements a Model Context Protocol server over stdio.
mcpclient
Package mcpclient implements an MCP client that connects to external MCP servers over stdio.
Package mcpclient implements an MCP client that connects to external MCP servers over stdio.
memory
Package memory provides persistent, agent-managed memory across sessions.
Package memory provides persistent, agent-managed memory across sessions.
narrate
Package narrate produces human-friendly, emoji-rich transition messages describing what the agent is doing.
Package narrate produces human-friendly, emoji-rich transition messages describing what the agent is doing.
redact
Package redact provides secret detection and redaction for odek output.
Package redact provides secret detection and redaction for odek output.
render
Package render provides emoji-driven terminal rendering for the odek agent loop.
Package render provides emoji-driven terminal rendering for the odek agent loop.
resource
Package resource implements @-prefixed resource discovery and inline resolution.
Package resource implements @-prefixed resource discovery and inline resolution.
sandbox
Package sandbox builds and operates the Docker container that isolates the agent's shell and file-tool execution from the host.
Package sandbox builds and operates the Docker container that isolates the agent's shell and file-tool execution from the host.
session
Package session persists agent conversation history across runs.
Package session persists agent conversation history across runs.
skills
Package skills — advanced skill matching using scoring-based approach.
Package skills — advanced skill matching using scoring-based approach.
telegram
Package telegram provides Telegram bot integration.
Package telegram provides Telegram bot integration.
tool
Package tool provides the clarify tool — ask the user a question and wait for a response.
Package tool provides the clarify tool — ask the user a question and wait for a response.
transport
Package transport provides tuned HTTP transports for odek's API clients.
Package transport provides tuned HTTP transports for odek's API clients.
ws
Package ws provides WebSocket constants used by cmd/odek/serve.go.
Package ws provides WebSocket constants used by cmd/odek/serve.go.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL