Connectors¶
Every connector type, what it reads, its options with their defaults, and what it keeps.
A connector is one [[connectors]] block of jezero.toml; its name becomes the
source of the items it produces.
Types¶
| Type | Status | Reads | Section |
|---|---|---|---|
claude_code |
available | Claude Code transcripts (*.jsonl under a projects folder) |
claude_code |
codex |
available | Codex CLI rollouts (*.jsonl, *.jsonl.zst under a sessions folder) |
codex |
opencode |
available | OpenCode's SQLite store, or session exports uploaded by a device | opencode |
hermes |
available | Hermes Agent's state.db, or session exports uploaded by a device |
hermes |
antigravity |
available | Antigravity's local store, or transcripts uploaded by a device | antigravity |
paperclip |
available | Paperclip tasks: issues with comments, documents and decisions, over its API | paperclip |
jsonl |
available | Pre-summarised items, loaded by jezero import-jsonl; a [[connectors]] block of this type is refused |
jsonl |
gmail |
planned | Not implemented: listed so check-config can tell it from a typo |
gmail |
jezero check-config lists the configured connectors and marks planned types as "not
implemented yet". A type that is not in this table is a configuration error.
Other packages can add types through the jezero.connectors entry point group, pointing at
a ConnectorType:
A built-in type wins over an add-on with the same name; an add-on that fails to load is skipped with a warning.
Common keys¶
These keys exist for every type. They are validated with the rest of the configuration (see
[[connectors]]); everything type-specific goes in
[connectors.options].
| Key | Type | Default | Meaning |
|---|---|---|---|
name |
string | required | Unique, ^[a-z0-9][a-z0-9_-]{0,63}$. Becomes the source of every item. |
type |
string | required | One of the types. |
enabled |
boolean | true |
jezero run without --connector skips disabled connectors; jezero health ignores them. |
interval_minutes |
integer (≥ 1) | 15 |
jezero run --due runs the connector only when this long has passed since its last run. |
project |
string | none | Project for items whose source gives none (see Projects). |
trust |
"own" | "external" |
the type's own | Copied to every item. external marks text written by third parties. Unset, it takes the type's default: external for gmail, own for every other type. |
options |
table | empty | Type-specific options, below. |
Option keys that look like a secret (api_key, token, secret, password, passwd,
credentials, private_key, alone or as a suffix) are rejected unless they end in _env
and name an environment variable.
Source names¶
The name is free, within the pattern above. Two conventions are used:
| Where the sessions are | Name | Example |
|---|---|---|
| On the server's own computer | agent-<agent> |
agent-claude-code, agent-codex |
| Uploaded by a device | <prefix>-<agent>-<device> |
agent-codex-laptop, work-codex-work-mac |
jezero device approve DEVICE SHA256 writes one disabled connector per agent for the
device, with the default prefix agent (change it with --source-prefix, see
jezero device approve). <agent> is the inbox name of the
agent, and each connector reads the device's inbox folder:
<agent> |
Connector type | Option written | Folder |
|---|---|---|---|
claude-code |
claude_code |
projects_dir |
<inbox>/<device>/claude-code/projects |
codex |
codex |
sessions_dir |
<inbox>/<device>/codex/sessions |
opencode |
opencode |
export_dir |
<inbox>/<device>/opencode/sessions |
hermes |
hermes |
export_dir |
<inbox>/<device>/hermes/sessions |
antigravity |
antigravity |
transcripts_dir |
<inbox>/<device>/antigravity/brain |
Client keys are limited to sources by glob (sources = ["agent-*"]), so the prefix decides
which keys can read a device's sessions.
How a run works¶
The same steps apply to every scheduled type.
- Collect. The connector lists the units (sessions, conversations, issues) changed
since its cursor. The cursor is a timestamp: a file's modification time, a session's
last message time, or an issue's last activity. A folder or file named in the options
that does not exist fails the run with
OPTION folder not found: PATH(orfile not found), so a typo shows up injezero healthinstead of as an empty source. - Settle. Units changed in the last
settle_minutesare deferred to a later run, so an ongoing session is not summarised every few minutes.jezero runreports them asdeferred_sessions. - Render. Each unit becomes plain text, one line per message or tool call, each line
starting with its UTC time:
[2026-10-21 09:12] user: .... Plumbing is dropped (see each type). - Segment. Text longer than
summarizer.max_input_chars(default 60000) is split into segments, one item each. - Redact, compare, summarise. Secrets are redacted. A segment whose redacted text has
the same SHA-256 as the stored item is skipped as
unchanged; the others go to the summarizer and are inserted or updated. - Prune. When a unit is read again whole and no longer produces some of its stored
segments (
max_input_charschanged, or the session got shorter), those items are removed and counted inpruned. Nothing is pruned for a unit with a failed segment, and a unit the source stops producing altogether is kept. - Advance. The cursor moves only when every segment was stored and the connector
reported no warning. Otherwise the same units are read again next run, and the error is
kept in the connector's state for
jezero health.
--limit N processes at most N units, oldest first. --sample N processes the newest N
units as a pilot: it stores their items but leaves the cursor, the last run and the last
success where they were, and needs no summarizer when nothing is ready (see jezero
run). jezero connector reset
forgets the cursor, so the next run reads the whole source again; unchanged units are
skipped by their hash.
Projects¶
Agent connectors find each item's project from the folder the session ran in, with the
rules of [projects], first match wins:
- The
<session file>.projectfile next to an uploaded session: the project the device's client found (see the client). [projects].paths: an explicit folder, longest match.- The git repository holding the folder (the main repository for a worktree), when the
folder exists on the server and
[projects].gitis notfalse. [projects].roots: the folder right below one of these names, case insensitive. The default list isprojects,code,dev,src,repos,repositories,git,github,gitlab,workspace,workspaces,work,developerandsites; settingrootsreplaces it.
The result is lowercased, then renamed by [projects].aliases merged with the connector's
project_aliases option (the option wins). If no rule matches, the connector's project
key is used, and if that is not set either, the summarizer's guess. paperclip uses its
own project_map instead of the folder rules, then renames the result with the same
aliases.
Prompt cleaning¶
codex, opencode and antigravity accept two options for orchestrators that repeat the
same long instructions at the top of every prompt. Both apply to user messages only.
| Option | Type | Default | Meaning |
|---|---|---|---|
user_prompt_markers |
list of regex | [] |
Keep a message only from the first match of the first marker that matches (multiline mode). |
user_prompt_drop_lines |
list of regex | [] |
Drop every line that matches one of them, after the markers. |
Read-only commands¶
codex, opencode and antigravity drop shell commands that only inspect, unless
keep_read_only_commands = true. A command is read-only when it starts with sed -n,
rg, grep, cat, head, tail, ls, nl, wc, find, pwd, tree, stat,
file, which, or git status|diff|log|show|branch|rev-parse|ls-files.
claude_code¶
Claude Code session transcripts. Claude Code writes one JSON Lines file per session under a projects folder, one subfolder per working directory, and deletes old ones by itself (30 days by default): the library keeps the text once it is ingested.
- Unit: one
*.jsonlfile, found recursively underprojects_dir. Cursor: the file's modification time. - Kept: user and assistant text; one line per tool call (
tool call: NAME ARGS); the result of the tools inkeep_results_of(Task result: ...); slash commands as(command) NAME ARGS; command output as(output) ..., shortened. - Dropped:
<system-reminder>and<memory-context>blocks, meta records, "Caveat: The messages below were generated by the user..." notices, other tool results, side-chain records andsubagents/folders (unlessinclude_subagents). - Title: the session's
ai-titlerecord, else itssummaryrecord.
| Option | Type | Default | Meaning |
|---|---|---|---|
projects_dir |
path | required | Claude Code's projects folder, usually ~/.claude/projects. Missing folder: the run fails. |
settle_minutes |
number | 30 |
Wait until a file has not changed for this long. |
include_subagents |
boolean | false |
Also read subagents/ transcripts and side-chain records. |
include_tool_calls |
boolean | true |
One line per tool call. |
max_tool_args_chars |
integer | 200 |
Tool call arguments (as JSON) are cut here, with .... |
keep_results_of |
list of string | ["Task", "Agent"] |
Tools whose result is kept: the report of a delegated task. |
max_kept_result_chars |
integer | 3000 |
Kept results are shortened in the middle to this length. |
max_command_output_chars |
integer | 500 |
Local command output is shortened in the middle to this length. |
skip_entrypoints |
list of string | [] |
Skip sessions whose entrypoint is listed (for example sdk-cli). |
skip_project_dirs |
list of glob | [] |
Skip sessions whose project folder (Claude Code's slug of the working directory) matches. |
min_chars |
integer | 400 |
Skip sessions whose rendered text is shorter. |
project_aliases |
table of string | {} |
Rename detected projects, on top of [projects].aliases. |
| Item field | Value |
|---|---|
external_id |
<sessionId>#<line>: the 0-based line of the segment's first record |
original_ref |
claude-code-session:<sessionId> |
| Project folder | the session's cwd |
[[connectors]]
name = "agent-claude-code"
type = "claude_code"
interval_minutes = 15
[connectors.options]
projects_dir = "~/.claude/projects"
settle_minutes = 30
keep_results_of = ["Task", "Agent"]
min_chars = 400
skip_project_dirs = ["-tmp-*"]
codex¶
OpenAI Codex CLI sessions. Codex writes one JSON Lines "rollout" per session under
sessions/YYYY/MM/DD/; newer builds compress them (.jsonl.zst).
- Unit: one
*.jsonlor*.jsonl.zstfile, found recursively undersessions_dir. Cursor: the file's modification time. - Kept: user and assistant messages; one short line per tool call:
shell: CMD ; CMDfor commands,patch: add PATH, update PATHfor patches,tool call: NAME ARGSfor the rest. - Dropped: developer and system messages; text Codex injects into user messages
(
<environment_context>,<recommended_plugins>,<user_instructions>,<skills_instructions>,<permissions instructions>,<turn_aborted>,# AGENTS.md instructions, and blocks not marked as typed by the user); reasoning; tool output; token accounting; polling of running commands (write_stdin); read-only commands; sessions started by Codex as sub-agents (unlessinclude_subagents). - Title: the session's
thread_namefromsession_index.jsonl.
| Option | Type | Default | Meaning |
|---|---|---|---|
sessions_dir |
path or glob | required | Codex's sessions folder, usually ~/.codex/sessions. A glob (~/agents/*/codex-home/sessions) follows several homes, including ones added later. A missing folder fails the run. A glob that matches nothing yet does not, but the folder before its first wildcard must exist (sessions_dir folder not found: /home/me/agents). |
session_index |
path | <sessions_dir>/../session_index.jsonl |
Where session titles are read from. |
settle_minutes |
number | 30 |
Wait until a file has not changed for this long. |
include_subagents |
boolean | false |
Also read sessions whose session_meta.source marks a sub-agent. |
include_tool_calls |
boolean | true |
One line per tool call. |
max_tool_args_chars |
integer | 200 |
Tool call lines are cut here, with .... |
keep_read_only_commands |
boolean | false |
Keep read-only commands. |
skip_tools |
list of string | ["wait", "sleep", "list_agents"] |
Tool names never shown. |
skip_cwd_globs |
list of glob | [] |
Skip sessions whose working directory matches. |
min_chars |
integer | 400 |
Skip sessions whose rendered text is shorter. |
project_aliases |
table of string | {} |
Rename detected projects, on top of [projects].aliases. |
user_prompt_markers |
list of regex | [] |
See Prompt cleaning. |
user_prompt_drop_lines |
list of regex | [] |
See Prompt cleaning. |
| Item field | Value |
|---|---|
external_id |
<session id>#<line>: the 0-based line of the segment's first record |
original_ref |
codex-session:<session id> |
| Project folder | cwd of the session_meta record |
[[connectors]]
name = "agent-codex"
type = "codex"
interval_minutes = 15
[connectors.options]
sessions_dir = "~/.codex/sessions"
skip_cwd_globs = ["*/scratch-*"]
project_aliases = { "shop-wt" = "shop" }
opencode¶
OpenCode sessions, from its SQLite store (opened read-only; the event table is never
read) or from the session exports the OpenCode plugin uploads from another computer.
- Unit: one session. Cursor: the session's
time_updated(store) or the export file's modification time (export_dir). - Kept: user and assistant text; one line per tool call that changes something:
shell: CMD,edit: PATH(alsowrite,patch,multiedit),tool call: NAME ARGSfor the rest, with(failed)when the call ended in an error. - Dropped: reasoning, step markers, patches, snapshots, tool output, the tools
read,grep,glob,list,todoread,webfetchandinvalidand read-only commands (unlesskeep_read_only_commands), sub-agent sessions (unlessinclude_subagents). - Title: the session's title.
| Option | Type | Default | Meaning |
|---|---|---|---|
db_path |
path | ~/.local/share/opencode/opencode.db |
OpenCode's database. Missing file: the run fails. |
export_dir |
path | none | Read *.json exports in this folder instead of the database. Missing folder: the run fails. |
settle_minutes |
number | 30 |
Wait until a session has not changed for this long. |
include_subagents |
boolean | false |
Also read sessions that have a parent session. |
include_tool_calls |
boolean | true |
One line per tool call. |
max_tool_args_chars |
integer | 200 |
Tool call lines are cut here, with .... |
keep_read_only_commands |
boolean | false |
Keep read-only tools and read-only commands. |
skip_tools |
list of string | ["todowrite"] |
Tool names never shown. |
skip_dir_globs |
list of glob | [] |
Skip sessions whose directory matches. |
min_chars |
integer | 400 |
Skip sessions whose rendered text is shorter. |
project_aliases |
table of string | {} |
Rename detected projects, on top of [projects].aliases. |
user_prompt_markers |
list of regex | [] |
See Prompt cleaning. |
user_prompt_drop_lines |
list of regex | [] |
See Prompt cleaning. |
An export is {"session": {...}, "messages": [{"info": {...}, "parts": [...]}]}, as the
OpenCode SDK returns them.
| Item field | Value |
|---|---|
external_id |
<session id>#<n>: the 0-based position of the segment's first rendered block |
original_ref |
opencode-session:<session id> |
| Project folder | the session's directory |
[[connectors]]
name = "agent-opencode"
type = "opencode"
interval_minutes = 15
[connectors.options]
db_path = "~/.local/share/opencode/opencode.db"
skip_dir_globs = ["/tmp/*"]
hermes¶
Hermes Agent sessions, from a profile's state.db (opened read-only) or from the session
exports the Hermes plugin uploads from another computer. Set exactly one of state_db and
export_dir; both or neither is an error.
- Unit: one session. Cursor: the time of its last message (
state_db) or the export file's modification time (export_dir). - Kept: user and assistant messages (only active ones, when the database marks them);
one line per assistant tool call (
tool call: NAME ARGS). - Dropped: tool results,
<memory-context>and<system-reminder>blocks,[System note: ...]notes, sessions whosesourceis inskip_session_sources. - Title: the session's title.
| Option | Type | Default | Meaning |
|---|---|---|---|
state_db |
path | none | A Hermes profile's state.db. Missing file: the run fails. |
export_dir |
path | none | Read <session>.json exports in this folder. Missing folder: the run fails. |
skip_session_sources |
list of string | ["cron", "subagent"] |
Session sources (channels) to skip. |
settle_minutes |
number | 30 |
Wait until a session has been quiet for this long. |
include_tool_calls |
boolean | true |
One line per tool call. |
max_tool_args_chars |
integer | 200 |
Tool call arguments are cut here, with .... |
min_chars |
integer | 400 |
Skip sessions whose kept dialogue is shorter, such as a lone greeting. |
project_aliases |
table of string | {} |
Rename detected projects, on top of [projects].aliases. |
With --sample N, sessions skipped by min_chars do not count towards N.
The export format is described in the client reference.
| Item field | Value |
|---|---|
external_id |
<session id>#<message id>: the database id of the segment's first message |
original_ref |
hermes-session:<session id> |
| Project folder | the session's cwd, when the database has that column |
[[connectors]]
name = "agent-hermes"
type = "hermes"
interval_minutes = 15
[connectors.options]
state_db = "~/.hermes/state.db"
skip_session_sources = ["cron", "subagent"]
antigravity¶
Antigravity conversations, from its local store or from the transcripts the Antigravity plugin uploads from another computer.
The local store keeps one SQLite file per conversation, <data_dir>/conversations/<id>.db,
with protobuf steps and no published schema. The connector reads only the fields observed to
be stable (user input, agent replies, tool calls with their short toolSummary) and skips
whatever does not parse. Titles and workspaces come from
<data_dir>/conversation_summaries.db when it exists.
An uploaded transcript is <conversation id>/transcript.jsonl, Antigravity's JSON log:
USER_INPUT steps (the text inside <USER_REQUEST>) and PLANNER_RESPONSE steps.
- Unit: one conversation. Cursor: the file's modification time.
- Kept: user requests, agent replies, one line per tool call:
shell (SUMMARY): CMD,edit: PATH,web search: QUERY,read url: URL, elseNAME: SUMMARYwhen the call has a summary. - Dropped: tool results, thinking notes, task notifications, context checkpoints, the
file-reading tools (
view_file,read_file,list_dir,grep_search,find_by_name) and read-only commands (unlesskeep_read_only_commands), any other tool call without a summary. - Title: from
conversation_summaries.db(local store only).
| Option | Type | Default | Meaning |
|---|---|---|---|
data_dir |
path | ~/.gemini/antigravity-cli |
Antigravity's data folder. |
transcripts_dir |
path | none | Read */transcript.jsonl here instead of data_dir. |
settle_minutes |
number | 30 |
Wait until a conversation has not changed for this long. |
include_tool_calls |
boolean | true |
One line per tool call. |
max_tool_args_chars |
integer | 200 |
Tool call lines are cut here, with .... |
keep_read_only_commands |
boolean | false |
Keep file-reading tools and read-only commands. |
skip_dir_globs |
list of glob | [] |
Skip conversations whose workspace matches (local store only). |
min_chars |
integer | 400 |
Skip conversations whose rendered text is shorter. |
unreadable_min_steps |
integer | 2 |
A conversation with at least this many steps and nothing readable raises a warning. |
project_aliases |
table of string | {} |
Rename detected projects, on top of [projects].aliases. |
user_prompt_markers |
list of regex | [] |
See Prompt cleaning. |
user_prompt_drop_lines |
list of regex | [] |
See Prompt cleaning. |
A missing data_dir (data_dir folder not found: PATH), a data_dir without a
conversations folder (data_dir has no conversations folder: PATH) or a missing
transcripts_dir fails the run. When nothing can be
read from a conversation that has steps, the run reports format check: nothing readable in
N conversation(s) with steps (...); the Antigravity format may have changed: the
connector shows as failing in jezero health and its cursor stays, until the reader is
updated.
| Item field | Value |
|---|---|
external_id |
<conversation id>#<n>: the 0-based position of the segment's first rendered block |
original_ref |
antigravity-conversation:<conversation id> |
| Project folder | the first file:// workspace (local store); the .project file only (uploads) |
[[connectors]]
name = "agent-antigravity"
type = "antigravity"
interval_minutes = 15
[connectors.options]
data_dir = "~/.gemini/antigravity-cli"
skip_dir_globs = ["/tmp/*"]
paperclip¶
Paperclip tasks. Paperclip is an orchestrator for teams of agents; its issues hold what
was asked, the plan, each agent's report and the people's answers. The connector only
sends GET requests, with the key in Authorization: Bearer ***.
- Unit: one issue. Cursor: the later of its
lastActivityAtandupdatedAt. - Kept, in this order: a header (identifier, title, status, project, assignee, author) and the description; then comments, interactions (questions and confirmations with their answer) and documents (plans, runbooks, at their last revision), ordered by creation time, each with its author.
- Dropped: hidden issues, issues in
skip_statuses, deleted and empty comments, system notices (unlessskip_system_notices = false). - Title:
<identifier> <title>.
| Option | Type | Default | Meaning |
|---|---|---|---|
api_url |
URL | "http://127.0.0.1:3100/api" |
Paperclip's API. |
api_key_env |
string | "PAPERCLIP_API_KEY" |
Environment variable holding the API key. Unset: the run fails. An agent key scoped to one company is enough. |
company_ids |
list of string | [] |
Companies to read. Empty: the company of the key's agent (GET /agents/me). |
settle_minutes |
number | 30 |
Wait until an issue has been quiet for this long. |
include_documents |
boolean | true |
Add the issue's documents. |
include_interactions |
boolean | true |
Add the issue's interactions. |
skip_system_notices |
boolean | true |
Drop comments presented as system notices. |
skip_statuses |
list of string | [] |
Issue statuses to skip. |
project_map |
table of string | {} |
Paperclip project name → Jezero project. |
project_aliases |
table of string | {} |
Rename the mapped projects, on top of [projects].aliases (matched case-insensitively). |
board_name |
string | "board" |
Name shown for comments written by people. |
max_block_chars |
integer | 8000 |
A description, comment, interaction or document is shortened in the middle to this length. |
min_chars |
integer | 200 |
Skip issues whose rendered text is shorter. |
Requests, per run: /agents/me (only without company_ids),
/companies/<id>/issues?limit=10000, /companies/<id>/agents, /companies/<id>/projects
(failures ignored), and per issue /issues/<id>, /issues/<id>/comments,
/issues/<id>/interactions and /issues/<id>/documents. Each request times out after 30
seconds.
| Item field | Value |
|---|---|
external_id |
<identifier>#<n>: 0 for the segment that starts with the header, else the position of its first entry |
original_ref |
paperclip-issue:<identifier> |
occurred_at |
the latest time among the segment's entries |
project |
project_map[<project name>], renamed by the aliases, else the connector's project, else the summarizer's guess |
[[connectors]]
name = "paperclip"
type = "paperclip"
interval_minutes = 15
[connectors.options]
api_url = "http://127.0.0.1:3100/api"
api_key_env = "JEZERO_PAPERCLIP_API_KEY"
project_map = { "Shop app" = "shop" }
skip_statuses = ["cancelled"]
jsonl¶
Items that are already summarised, in a JSON Lines file, one item per line. They are
loaded with jezero import-jsonl, which takes the source name
with --source; no [[connectors]] block is needed. The record shape is in
Data model.
jsonl cannot run on a schedule, so a [[connectors]] block of this type, enabled or
not, stops the configuration from loading:
Value error, connector 'notes': jsonl is import-only: use `jezero import-jsonl` instead of a [[connectors]] entry
gmail¶
Planned: Gmail threads through the API with the gmail.readonly scope. Mail is written by
third parties, so its items get trust = "external" unless the block says otherwise.
Nothing reads it yet: jezero run
reports it as "skipped": "not implemented yet" and jezero health ignores it. Its
options are not defined until it is implemented.
See also¶
- Configuration: the keys of
[[connectors]]and[projects]. - Add and tune sources: connectors in practice, pilots with
--sample. - Data model: what each item holds once stored.
- HTTP API: how devices fill the inbox these connectors read.