Tinker is a personal coding-agent harness — an interactive TUI (Terminal User Interface) and one-shot CLI that drives an LLM in an agent loop with file, search, shell, and MCP tools to read and modify a local workspace.
Tinker is designed for models that work over extremely long horizons — potentially as persistent agents that continue indefinitely, rather than as disposable chat sessions. Its architecture treats the model's context window as a bounded working set, not as the source of truth. Immutable canonical history, durable sessions, protocol-safe recovery, deterministic context revisions, Recall-addressable cold state, and context-pressure management allow work to continue across compaction, process restarts, and context-window limits.
This does not pretend that any model has infinite tokens or guarantee that it will recall every relevant fact. It means the harness is designed so that history remains durable and recoverable while the model repeatedly operates on a bounded, valid view of an ongoing session.
- Interactive TUI: Full terminal user interface with session management, prompt history, and slash commands.
- One-shot CLI: Run a single prompt non-interactively with
tinker run "prompt". - Built-in tools:
Glob/Grep— Find and search files by pattern or contentRead/Write/Edit— File I/O with content hashing and concurrent-modification protectionDelete— Delete one existing regular file without directory or symlink supportBash— Run foreground, background, and PTY shell commands with per-task working directoriesUpdatePlan— Track a complete ordered task plan and its progressTaskList/TaskOutput/TaskInput/TaskStop— Inspect, interact with, and stop long-running shell tasksWebSearch— Search the web via Exa APIWebFetch— Fetch and refine web page content (local, browser, or Exa backend)RecallSearch/RecallGet— Search or retrieve model-visible history from the current session or an explicitly selected session
- Agent Skills: Discover compatible
SKILL.mdpackages at project and user scope, disclose their catalog progressively, and keep activated instructions durable across context compaction and session resume. - MCP integration: Connect external Model Context Protocol servers — their tools are dynamically registered as
mcp__<server>__<tool>. - Session persistence: Sessions are persisted via SQLite, supporting session resume, history recall, and a catalog to browse and switch between sessions.
- Observation system: Tool execution results are formatted into structured text that the model sees, with separate raw results for event logs and TUI display.
- Turn cancellation: Users can cancel an ongoing turn safely, with protocol-safe synthetic tool messages.
- Context metering: Budget-aware context management with protocol validation before sending requests to the model.
- Deterministic context compaction: Idle sessions can swap eligible historical tool output into Recall-addressable placeholders without calling the model.
- Infinite Context architecture: Immutable canonical history, deterministic context revisions, Recall-addressable cold state, and protocol-safe prefix retirement support sessions designed to continue indefinitely without pretending the model has infinite tokens.
- Choice of models: Uses an OpenAI-compatible Chat Completions transport with explicit model and context limits. Actual provider support must be established by a qualification matrix; transport compatibility alone is not a guarantee.
When automatic retries for a provider request are exhausted, the TUI offers
Retry again or End this turn. Retrying reuses the same request within the
same turn and iteration, with a fresh automatic retry allowance. Ending the turn
(or pressing Esc in the selection) records the original failure. The selection
adds no conversation message and applies only to the current running process.
Retryable failures include HTTP 429/500/502/503/504, structured server_error
and rate_limit_exceeded errors inside provider streams or failed Responses,
and transient connection failures, including socket resets during streaming.
Tool failures and non-retryable errors keep their existing behavior. One-shot runs do not wait for this TUI selection; service sessions expose it to
connected clients.
Bash accepts optional cols and rows for the initial PTY screen size:
{"command":"python3 -q","tty":true,"cols":160,"rows":48}Each omitted dimension keeps its default: 80 columns and 24 rows. Columns accept
integers from 2 to 1000; rows accept integers from 1 to 1000. When tty is omitted
or false, dimensions are ignored and execution continues through ordinary pipes.
TaskOutput and TaskInput report the actual screen size. Dimensions are set at
creation; running tasks cannot be resized through these tools.
Both Recall tools accept an optional sessionId (a canonical session UUID).
Omitting it keeps the current-session behavior. In the TUI or one-shot CLI, the
agent can use a session ID you supply or find in a memory record's filename/header:
RecallSearch({ sessionId: sourceSessionId, query: "distinctive-anchor" })
RecallGet({ sessionId: sourceSessionId, source: "ctx://message/<message-UUID>" })Reuse the same sessionId for Get and subsequent pages. Search's
snapshot_through_ordinal, ordinals and turn numbers belong to that session only.
Results identify their source session and workspace. Missing, ambiguous, unsafe,
unsupported or corrupt sessions return errors; Recall never silently switches
back to the current session. Memory is not required.
Data scope: explicit session selection can read other workspaces under the
configured Tinker home (TINKER_HOME overrides the OS home). Retrieved text is
sent to the current model provider and saved as a tool result in the current
session, including when projects or providers differ. There is no additional
cross-workspace confirmation dialog.
External history uses short read-only SQLite snapshots, without resuming the source session, taking its execution lock, repairing indexes or migrating its schema. Only the current readable schema is supported. Active writers' committed history is readable; an open/interrupted turn may be incomplete. Historical instructions, Skills and authorizations are not activated, and content hashes prove storage consistency, not truth. Use Read/Grep to verify current facts. Original workspaces need not still exist. SQLite WAL reads can coordinate through SHM; read-only access does not promise that an active writer's directory remains byte-for-byte unchanged. See the history access implementation.
Automatic swap and prefix retirement are product defaults, independent of model names, evaluation scores, report availability, and tool-description hashes. Under context pressure, the runtime tries swap first and attempts retirement only when swap has no eligible candidates or insufficient candidates. Existing scheduling, protocol boundaries, cancellation, transaction, and request/surface consistency checks remain in force. Required Recall tools and the current retirement contract must be available; enabling a product policy does not bypass execution safety. There are no new user-facing switches or model allowlists.
Evaluation measures model behavior; it does not enable or disable these features.
New evaluation reports (active-recall-qualification-v2) contain metrics, gates,
and provenance but no automation flags. Historical reports remain unchanged. The
session-selection evaluation
retains its measured failure: Search → Get missed the threshold even though all
Recall-only tasks passed. Its old automation fields are legacy data, not runtime
policy. The subsequent description cleanup has not been re-evaluated with a live
model.
If you already use a coding agent, you can ask it to read Tinker's documentation, install the package, and prepare a complete local configuration for you. Values it cannot safely determine can be left as clearly named placeholders; when setup is complete, the agent should tell you exactly which file to edit and what each placeholder expects.
Copy and send this prompt to your existing agent:
Open https://github.com/ishowshao/tinker and read Tinker's README and linked model-configuration documentation, then install and configure Tinker on this machine.
Create a complete model profile configuration in an appropriate local file. Use clearly named placeholders for any required values that cannot be determined from the documentation or the current environment; do not invent API credentials, endpoint URLs, model names, or model limits. Configure Tinker to use that file, and verify the installation and configuration as far as possible without making a live model request.
When finished, tell me:
1. the exact path of the configuration file you created;
2. every placeholder I still need to replace;
3. what value each placeholder expects;
4. any command I need to run after filling them in;
5. the command to start Tinker.
Do not ask me for the missing values during setup unless proceeding would be unsafe. Prefer creating a structurally complete configuration with placeholders so I can fill them in locally afterward.
npm install --global tinker-agent
# Start the interactive TUI
tinker
# Run a one-shot prompt
tinker run "explain the project structure"
# Inspect the installed command surface
tinker --help
tinker --versionThe npm package includes its own Bun runtime. To run Tinker from a source checkout,
install Bun 1.3.14 or later, then use bun install followed by bun run tinker.
On macOS/Linux, the default TUI starts or reuses a shared local service. First
launch uses OpenSSL to prepare private local credentials under
<home>/.tinker/service/ (TINKER_HOME or the OS home), and automatically registers
the current workspace. Exiting the TUI leaves accepted tasks running; use
/resume to reconnect. tinker --local explicitly runs the independent TUI;
run remains independent. Existing .data service configurations remain available
through explicit connect. See local startup and configuration
for configuration, environment changes and lifecycle limits. Optional macOS login
startup and crash restart are available through tinker serve --install;
serve --stop, --restart and --uninstall manage the resident service.
run accepts exactly one Prompt source: one shell-quoted argument, explicit stdin,
or a UTF-8 text file. Use stdin for multiline code, private patches, or Prompt text
containing secrets so it does not become a command-line argument:
printf '%s\n' 'Review `$HOME`, *.ts, and "quotes" literally.' | tinker run --stdin
tinker run --file prompts/review.mdTinker does not reconstruct shell expansions or join multiple positional arguments.
Use tinker run "one quoted prompt"; the former unquoted variadic form now fails
with a usage error. File paths are resolved from the current directory and may point
outside the configured workspace. You are responsible for Prompt-file permissions
and cleanup.
The installed package exposes this public CLI:
| Command | Description |
|---|---|
tinker |
Start the full TUI through the shared local service; exiting detaches only. |
tinker serve --install |
Install and start a macOS per-user LaunchAgent with crash restart. |
tinker serve --uninstall |
Drain, stop and remove the LaunchAgent; retain history and configuration. |
tinker serve --stop |
Drain and stop the service; disable supervisor restart until the next start. |
tinker serve --restart |
Drain and restart with the current executable and configuration. |
tinker serve --restart --force |
Allow interruption after the configured shutdown grace period. |
tinker --local |
Run the TUI independently without a service (explicit fallback). |
tinker --profile <profile-name> |
Start the TUI with a selected model profile. |
tinker run [--profile <profile-name>] [--yolo] <prompt> |
Submit one shell-quoted prompt argument. |
tinker run [--profile <profile-name>] [--yolo] --stdin |
Read the prompt from standard input until EOF. |
tinker run [--profile <profile-name>] [--yolo] --file <path> |
Read the prompt from a UTF-8 text file. |
tinker update |
Update the global npm installation from the official npm registry. |
tinker serve [--config <path>] |
Run the local daemon for paired remote clients. |
tinker serve [--config <path>] --background |
Discover or start one detached service for this state directory, and print its address as JSON. |
tinker serve [--config <path>] --status |
Probe the local service without starting it; print JSON and exit 1 when offline. |
tinker connect --config <path> |
Attach a terminal client to a service; exiting detaches only. |
tinker connect --config <path> --tui [--workspace <id>] [--session <id>] |
Use the full TUI for service sessions and task execution. |
tinker connect --config <path> --tui --service-config <path> [--session <id>] |
With --tui, discover/start this local service and register the current directory if needed. |
tinker --help |
Show top-level CLI help. |
tinker help run |
Show one-shot command help. |
tinker help update |
Show update command help. |
tinker --version |
Print the installed package version. |
tinker update is available only to direct npm global installations. It checks
the latest stable version on the official npm registry, updates that same global
prefix, and exits without loading model configuration or starting a session.
Idle services using that installation are safely stopped before installation and
restarted with the new version afterward. If any affected service is busy, the
update is postponed without interrupting its work; retry tinker update after it
finishes. Older services without idle-only shutdown support must be stopped
manually first. Installation or restart failures include manual recovery commands.
Services started from a different installation, including a source checkout, are
not restarted by the global updater.
Repository development commands are separate from the installed CLI:
bun install # Install dependencies
bun run tinker # Start the interactive TUI
bun test # Run test suite
bun run typecheck # TypeScript type checking (tsc --noEmit)
bun run lint # ESLint (zero warnings required)
bun run format # Biome code formatting
bun run check # Full repository quality gatebun run check runs type checking, formatting verification, linting, the README
contract check, the full test suite, and the benchmark smoke check. Documentation
checking is read-only; use bun run docs:generate explicitly after changing a
public declaration.
Tinker scans <workspace>/.agents/skills/ and ~/.agents/skills/ once when a new
or resumed runtime starts. Each direct child skill must contain a strictly valid
SKILL.md; an invalid discovered skill stops activation with a clear error. A
project skill wins when both scopes define the same name.
Only skill names and descriptions are initially exposed to the model. When the
model selects a matching skill through the conditional Skill tool, Tinker sends
the complete snapshotted SKILL.md and a bounded listing of its standard resource
directories. Relative resource paths are resolved from that skill's directory and
are read or executed only through Tinker's existing tools. Activated instructions
remain in the current system surface across later turns and compaction; resume
rebinds them to the current validated files.
Skill content is not local-only after activation: it is sent to the configured
model provider and persisted in the private session SQLite database, event log,
and observation log, just like other model-visible file content. /skills is
read-only and does not rescan, install, activate, or remove skills.
Set TINKER_MODELS to use a profile file. Without it, the env-mode model fields
are required. Boolean environment values accept case-insensitive true/false,
1/0, yes/no, and on/off.
| Variable | Area | Applies | Required | Type | Default | Secret | Description |
|---|---|---|---|---|---|---|---|
TINKER_MODELS |
Model | All modes | No | Non-empty string | — | No | Optional model profiles JSON path. A leading ~ expands to the home directory; other relative paths resolve from the process cwd. |
TINKER_MODEL |
Model | Env mode | Env mode | Non-empty string | — | No | Model name used when model profiles are not configured. |
TINKER_API |
Model | Env mode | No | Non-empty string | "chat-completions" |
No | Model API adapter: "chat-completions" or "responses". |
TINKER_BASE_URL |
Model | Env mode | Env mode | Non-empty string | — | No | OpenAI-compatible API root URL; do not append /chat/completions or /responses. |
TINKER_API_KEY |
Model | Env mode | Env mode | Non-empty string | — | Yes | API credential for the configured model endpoint. |
TINKER_CONTEXT_WINDOW_TOKENS |
Model | Env mode | Env mode | Positive integer | — | No | Model context-window size in tokens. |
TINKER_MAX_SUPPORTED_OUTPUT_TOKENS |
Model | Env mode | Env mode | Positive integer | — | No | Maximum output-token count supported by the model; must not exceed the context window. |
TINKER_INCLUDE_REASONING_CONTENT |
Model | Env mode | No | Boolean | false |
No | Replay provider reasoning_content in Chat Completions history; ignored by Responses. |
TINKER_STREAM |
Model | Env mode | No | Boolean | true |
No | Use streaming transport for the selected model API. |
TINKER_WEBFETCH_REFINE_MODEL |
Model | Env mode | No | Non-empty string | — | No | Optional WebFetch refiner model; currently must match TINKER_MODEL. |
TINKER_WORKSPACE |
Workspace | All modes | No | Non-empty string | Process cwd | No | Workspace path. A leading ~ expands to the home directory; other relative paths resolve from the process cwd. |
TINKER_MAX_ITERATIONS |
Workspace | All modes | No | Positive integer | 65536 |
No | Maximum agent-loop iterations per turn. |
EXA_API_KEY |
Tooling | All modes | No | Non-empty string | — | Yes | Enables WebSearch and the Exa WebFetch backend when set. |
TINKER_MCP_TIMEOUT_MS |
Tooling | All modes | No | Positive integer | 60000 |
No | MCP tool-call timeout in milliseconds. |
TINKER_MCP_MAX_OBSERVATION_CHARS |
Tooling | All modes | No | Positive integer | 40000 |
No | Maximum model-visible characters in one MCP result. |
TINKER_BASH_DEFAULT_TIMEOUT_MS |
Tooling | All modes | No | Positive integer | 60000 |
No | Default Bash foreground timeout in milliseconds. |
TINKER_BASH_MAX_TIMEOUT_MS |
Tooling | All modes | No | Positive integer | 600000 |
No | Maximum Bash foreground timeout in milliseconds. |
TINKER_YOLO |
Tooling | All modes | No | Boolean | false |
No | Allow high-confidence destructive Bash commands without confirmation. |
TINKER_GREP_TIMEOUT_MS |
Tooling | All modes | No | Positive integer | 20000 |
No | Bundled ripgrep invocation timeout in milliseconds. |
TINKER_GREP_MAX_BUFFER_BYTES |
Tooling | All modes | No | Positive integer | 20000000 |
No | Maximum buffered output from one ripgrep invocation. |
TINKER_WEBFETCH_REFINE_THRESHOLD |
Tooling | All modes | No | Positive integer | 2000 |
No | Content-length threshold that enables WebFetch refinement. |
TINKER_RIPGREP_PATH |
Tooling | All modes | No | Non-empty string | Bundled ripgrep | No | Explicit diagnostic override for the bundled ripgrep executable. |
Model and estimator API keys are sent to their configured external endpoints.
EXA_API_KEY is sent to Exa when WebSearch or the Exa WebFetch backend is used.
Keep configuration files private and review each service's data-handling policy.
Set TINKER_MODELS to point to a JSON file with multiple named model
profiles. When set, the profile's default field selects the startup model, and
the /model slash command (available in new sessions before any turns) lets you
switch to another profile. If TINKER_MODELS is not set, Tinker falls back to
the individual TINKER_* environment variables. A configured profiles file must
exist and be valid; Tinker does not silently fall back when it cannot be loaded.
For provider-specific examples and guidance on API adapters, model capabilities,
reasoning efforts, and context limits, see the
.tinker/models.json provider configuration guide.
Profile fields:
| Field | Required | Type / constraint | Default | Secret | Description |
|---|---|---|---|---|---|
model |
Yes | Non-empty string | — | No | Provider model name. |
api |
No | Non-empty string | "chat-completions" |
No | Model API adapter: "chat-completions" or "responses". |
apiBase |
Yes | Non-empty string | — | No | OpenAI-compatible API root URL; do not append /chat/completions or /responses. |
apiKey |
Yes | Non-empty string | — | Yes | API credential for this profile. |
contextWindowTokens |
Yes | Positive integer | — | No | Model context-window size in tokens. |
maxSupportedOutputTokens |
Yes | Positive integer | — | No | Maximum output-token count supported by the model; must not exceed contextWindowTokens. |
reasoning |
No | Object | — | No | Provider-specific reasoning efforts and the default for each new session runtime. |
includeReasoningContent |
No | JSON boolean | false |
No | Replay provider reasoning_content in Chat Completions history; ignored by Responses. |
stream |
No | JSON boolean | true |
No | Use streaming transport for the selected model API. |
inputModalities |
No | Normalized modality array | ["text"] |
No | Accepted model input modalities; normalizes to ["text"] or ["text", "image"]. |
toolResultModalities |
No | Normalized modality array | ["text"] |
No | Accepted tool-result modalities; normalizes to ["text"] or ["text", "image"]. |
reasoning fields:
| Field | Type / constraint | Description |
|---|---|---|
supportedEfforts |
Non-empty unique string array | Provider-supported effort values exposed by the /reasoning command. |
defaultEffort |
Non-empty string listed above | Effort used whenever a session runtime is created or reopened. |
The optional reasoning object declares provider-specific effort values. Efforts must be unique non-whitespace strings, reset is reserved by the TUI command, and defaultEffort must appear in supportedEfforts. Omitting reasoning sends no effort parameter and disables /reasoning for that profile.
Text-only profile example:
{
"default": "text",
"profiles": {
"text": {
"model": "example-text-model",
"api": "chat-completions",
"apiBase": "https://api.example.com/v1",
"apiKey": "your-model-api-key",
"contextWindowTokens": 128000,
"maxSupportedOutputTokens": 8192,
"reasoning": {
"supportedEfforts": [
"low",
"medium",
"high"
],
"defaultEffort": "medium"
},
"includeReasoningContent": false,
"stream": true,
"inputModalities": [
"text"
],
"toolResultModalities": [
"text"
]
}
}
}Image-capable profile example:
{
"default": "image",
"profiles": {
"image": {
"model": "example-vision-model",
"api": "responses",
"apiBase": "https://api.example.com/v1",
"apiKey": "your-model-api-key",
"contextWindowTokens": 128000,
"maxSupportedOutputTokens": 8192,
"reasoning": {
"supportedEfforts": [
"low",
"medium",
"high"
],
"defaultEffort": "medium"
},
"includeReasoningContent": false,
"stream": true,
"inputModalities": [
"text",
"image"
],
"toolResultModalities": [
"text",
"image"
]
}
}
}Set api to "responses" to use the standard Responses API. Keep apiBase
at the API root—such as https://api.openai.com/v1—because Tinker appends the
/responses route. Responses requests use the stateless common subset
(store: false with complete input history), so the same adapter works with
OpenAI and compatible providers that do not implement stored response chaining.
You can also select profiles explicitly for the TUI or one-shot command:
tinker --profile text
tinker run --profile text "explain the project structure"Switching models creates a new session. The previous session is preserved and
can be resumed with /resume. Each session records its profile name, and resume
reopens the session with that profile. Resume fails clearly if the profile is no
longer present or its runtime contract has changed. Older sessions without a
stored profile name can resume only when their model name uniquely matches one
configured profile.
Image attachment is enabled only for a profile whose inputModalities explicitly
includes image. In the interactive
TUI, type @ and select a file that is inside the workspace and visible to the
workspace search rules. One-shot commands, clipboard image bytes, remote URLs, and
files outside or ignored by the workspace search are not supported.
Tinker accepts PNG (not APNG), JPEG, and static WebP. It rejects GIF, animated WebP, and other formats. A message and provider request may contain at most eight images; each image may be at most 20 MiB, 4096 pixels on either edge, and 8,847,360 pixels in total. Provider requests preserve smaller images and proportionally downscale larger images to a maximum 2048-pixel long edge. Context planning uses fixed local token buckets derived from the materialized dimensions and performs no independent token-estimator request. See the image input policy and image asset store for the policy and storage implementations.
ViewImage(file_path) is registered only when the selected profile declares both
inputModalities: ["text", "image"] and
toolResultModalities: ["text", "image"]. The first implementation supports
image tool results through the Responses adapter; Chat Completions remains
text-only for tool results. Relative paths stay inside the workspace, absolute
paths may explicitly select an external local file, and symbolic links are
rejected. Canonical history stores content-addressed image references rather than
Base64, while stdout, TUI, Recall, and logs show deterministic text summaries.
See the ViewImage implementation for the tool's
validation and execution logic.
| Command | Description |
|---|---|
/status |
Show session and context details |
/skills |
Show available and active Agent Skills |
/mcp |
Show MCP servers and runtime tools |
/yolo [on|off] |
Show or change destructive Bash confirmation |
/memory |
Browse stored global memories |
/compact [retire] |
Swap tool output or retire a cold history prefix |
/undo |
Undo the latest Write/Edit/Delete turn |
/clear |
Start a new session and clear conversation |
/fork |
Clone the current session |
/view <path> |
View a local UTF-8 text file |
/copy |
Copy the last response as Markdown |
/model [profile-name] |
Switch model profile (new session) |
/reasoning [effort|reset] |
Show or change reasoning effort for this session runtime |
/resume [session-id] |
Choose or resume a session |
/session delete <session-id> --confirm |
Manage stored sessions |
/quit |
Exit the TUI |
/view opens a readable UTF-8 text file in a full-window viewer. Relative paths
must remain inside the workspace; absolute paths may point outside it. /compact
swaps eligible historical tool output, while /compact retire retires a complete
cold prefix whose original history remains available through Recall. /copy
copies the last completed assistant response as raw Markdown.
Profiles that declare reasoning expose /reasoning as a session-runtime
control. /reasoning <effort> temporarily selects one of the profile's
supportedEfforts, and /reasoning reset restores defaultEffort. The
selection is not written to configuration or canonical history: /clear,
/fork, /model, /resume, and a TUI restart create a new runtime from the
profile default. Responses requests send reasoning.effort; Chat Completions
requests send reasoning_effort. Press Ctrl+R while the prompt is idle to cycle
through supportedEfforts in profile order, wrapping from the last effort to the
first. When configured, the TUI information line shows the live effort immediately
after the model name, for example gpt-5.6-sol max.
The TUI loads optional project-scoped prompt aliases from .tinker.json in the
workspace root:
{
"version": 1,
"slashCommands": [
{
"name": "git-commit-and-push",
"description": "Commit and push workspace changes",
"prompt": "Please inspect the workspace changes, create an appropriate commit, and push it."
}
]
}Enter /git-commit-and-push to submit its configured prompt as an ordinary user
turn. Built-in slash commands appear first in suggestions, followed by project
commands in configuration order. Custom commands accept no arguments and cannot
override built-ins. The optional configuration is loaded once at TUI startup,
must be valid when present, and has a 1 MiB size limit. It is not loaded by the
one-shot tinker run command. See the
project command loader for configuration
validation and loading behavior.
Memory works without a model profile or embedding service in both the TUI and
one-shot CLI. Session transcripts are appended to
<home>/.tinker/memory/records/<sessionId>.md. MemoryCreate saves explicit notes
to memory/notes/<id>.md, using text as the heading and optional summary as
the body, with creation time and source workspace.
MemorySearch searches Markdown under <home>/.tinker/memory/ using literal,
case-insensitive keywords. Supply keywords (any keyword may match), optionally
context (default 3), limit (default 20 matching lines), and offset (default 0).
Each line counts once, even if several keywords match. Keywords cannot span lines.
Passages are grouped by file with line numbers; > marks matching lines and …
separates passages. Long lines retain up to 500 code points around the earliest
match. Use Read with offset and limit to expand a passage, or nextOffset to
continue searching. There are no regex, glob, output-mode, or multiline parameters.
Results follow file-path order; changes between calls can affect pagination.
There are no MemoryGet, MemoryUpdate, or
MemoryDelete tools. Resume appends to the same record; cloning copies historical
content; deleting a session leaves its memory record in place. /memory browses
previews of records and notes.
Old models.json memory configuration is ignored. Existing database and memory
diagnostic files are moved to <home>/.tinker/memory-legacy/<id>/ on first use;
they are preserved but not searched or converted. Older session-local
observations.md files move to records when those sessions are resumed.
See the memory design.
Tinker keeps private runtime state in a global home directory instead of inside
your projects. Each workspace maps to
~/.tinker/projects/<workspace-slug-hash>/ — the slug comes from the workspace
directory name and the hash from its canonical absolute path, so one project
always resolves to one storage directory even when reached through symlinks.
That directory holds per-session SQLite databases and event logs
(sessions/<id>/), background Bash task logs (bash/), imported image
assets (assets/images/), and prompt history (prompt-history.jsonl).
Workspaces stay clean: no .tinker/ directory is created inside them.
Set TINKER_HOME to relocate the global home (default: the OS home directory).
Global memory lives in <home>/.tinker/memory/ and the Chrome bridge host in
<home>/.tinker/chrome/. MCP server configuration is loaded from
<workspace>/.mcp.json; project slash commands are loaded from
<workspace>/.tinker.json.
Read has a fixed 262144-byte (256 KiB) content limit per call. A successful
call always returns the complete requested line range. Use offset and limit
to page through larger files; oversized requests fail instead of returning
truncated content.
Delete removes one existing regular file by workspace-relative or absolute path.
It rejects directories and symbolic links, and clears any in-memory file snapshot
only after the removal succeeds.
tinker/
├── src/
│ ├── cli/ # Entry points (tui, run), config
│ ├── agent/ # Agent loop, session ledger, turn cancellation, context metering
│ ├── tools/ # Tool executors (bash, glob, grep, read, write, edit, delete, recall, etc.)
│ ├── model/ # Model clients (OpenAI-compatible, fake), chat mapping, preflight
│ ├── mcp/ # MCP server management, tool executor adapter
│ ├── observation/ # Tool result → model-visible text
│ ├── skills/ # Agent Skills discovery, catalog, activation, and context
│ ├── session/ # SQLite session store, catalog, history reader, resume
│ ├── events/ # Event sinks, JSONL log, stdout printer
│ ├── tui/ # Ink/React UI components, projection store, session controller
│ ├── context/ # Context protocol validation, protocol frame construction
│ └── ids/ # Runtime ID generation (UUID v7)
├── docs/ # Design notes and planning documents
└── package.json
Runtime data lives in ~/.tinker/ (sessions, bash tasks, assets), not in the repo.
- Fast-fail: Validate assumptions early and return clear errors close to the source. Structured failures allow the model to correct and retry.
- Model sees only text: Tool execution results are rendered into readable text for the model. Raw result data with extra detail is kept for event logs and the TUI.
- Protocol safety: All tool calls produce protocol-safe messages — even cancellations, fatal errors, or interruptions generate well-formed tool messages so the agent loop can continue.
- Long-horizon continuity: Treat the context window as a replaceable working set over durable canonical history. Sessions should remain resumable and historically recoverable across compaction, interruption, and process restarts.
- Session durability: Every turn, iteration, and tool call is committed to the SQLite ledger before the model is called, enabling reliable resume and history recall.
- Node.js 20 or later and npm for the global package installation
- Bun 1.3.14 or later for development from source
- A configured OpenAI-compatible Chat Completions endpoint; transport compatibility alone does not establish provider qualification
Tinker is an agent with permission to read and modify files and run shell commands in the selected workspace. Review its configuration and model-provider data policies before using it with sensitive source code. Please report vulnerabilities privately through GitHub Security Advisories.
Contributions are welcome. See CONTRIBUTING.md for the development workflow and CODE_OF_CONDUCT.md for community expectations.
The original Tinker Infinite Context architecture and its initial implementation were designed and developed by ishowshao. See NOTICE for the attribution that accompanies distributions and derivative works.
Licensed under the Apache License 2.0.