One query language searches everything HarnessLink keeps for you: the Query page (Dashboard → Query, g q) searches history, memories, runs and session transcripts at once, and the search box of History, Archive, Memory and Runs takes the same syntax for that page alone. Plain words keep working: auth refactor finds both words.

text
auth refactor                         both words (full text)
"rate limit"                          the exact phrase
harness:codex                         a field equals a value
workspace:*superpi*                   wildcards: * any run, ? one character
cwd:/\/Workspace\/(api|web)/i         a regex on a field
/fix(ed)? the (cache|queue)/i         a regex on the main text
tokens:>10000 cost:>=1.5              numeric comparisons
created:2026-10-01..2026-10-26        a range (numbers or dates)
after:-7d before:2026-10-20           time bounds, relative or absolute
in:history in:runs                    sources (Query page only)
has:session -has:cwd                  the field is set / not set
-harness:omp NOT "draft"              exclusion
(harness:codex OR harness:omp) AND /timeout/i

Combining terms

Terms side by side must all match (an implicit AND). OR matches either side; NOT or a leading - excludes. Parentheses group.

Precedence, tightest first: parentheses, NOT, AND, OR. So harness:omp OR harness:codex timeout means "omp, or codex with timeout", while (harness:omp OR harness:codex) timeout means "timeout in omp or codex". NOT harness:omp timeout means "timeout, not in omp".

AND, OR and NOT are keywords only in capitals: this or that searches for three words.

Text

A bare word matches the page's main text: the prompt on History and Archive, the memory's body on Memory, the run id and session titles on Runs, the session title for transcripts on Query. History and memories use the full-text index: a word matches whole words (fix does not match fixed), case-insensitively. Add * for a prefix: fix* matches fix, fixed, fixes. Runs and transcripts match words as case-insensitive substrings.

"…" matches a phrase, in order. Inside quotes a backslash makes the next character literal (\" is a quote); nothing else is special.

Fields

field:value compares one field. The value may be quoted to include spaces (harness:"Claude Code"). Field names are a fixed list per page; an unknown one is an error that points at it.

PageFields
History, Archiveworkspace harness device session cwd prompt created
Memoryproject harness type device temp title body path updated
Runsharness phase roster workspace run tokens cost sessions updated
Queryall of the above, plus in:. Transcripts have workspace harness title session updated

Every page also accepts has:, before: and after:.

How a value matches depends on the field:

  • Labels (harness, phase, roster, type, workspace, project,

    device): equal, ignoring case. Names work as well as ids: harness:"Claude Code", type:"Work state", workspace:api (the label) or workspace:git:github.com/acme/api (the id), project: the project's friendly name or its folder slug, device: a hostname or device id.

  • Ids (session, run): exactly equal, case-sensitive.
  • Text (prompt, cwd, title, body, path): contains the value,

    ignoring case. prompt:Timeout matches "fix the timeout".

  • Numbers (tokens, cost, sessions): see below.
  • Times (created, updated): see below.
  • temp: temp:true or temp:false (also 1/0, yes/no).

On Runs, a run matches a session field when any of its sessions does (harness:codex), and -harness:codex keeps runs where none does. tokens (input + output + cache), cost, sessions (count) and updated (last activity) are the run's totals.

Wildcards

* matches any run of characters and ? exactly one, across the whole value: workspace:*superpi*, cwd:/src/*, session:s?. On text fields without a wildcard the value is a substring already. \* and \? are a literal star and question mark.

Numbers, times and ranges

Number and time fields take N, >N, >=N, <N, <=N or a range a..b (both ends included; either end may be left open: tokens:10000.., cost:..0.5).

A time is one of:

  • a date, 2026-10-01 (a whole UTC day), or a month, 2026-10;
  • a date and time in UTC, 2026-10-01T09:30 (that minute) or

    2026-10-01T09:30:15;

  • unix seconds, 1790812800;
  • relative to now: -30m, -2h, -7d, -2w, -3mo (30-day months),

    -1y; also -45s.

A time covers its whole span: created:2026-10-01 is that day, created:>2026-10-01 starts the next day, created:<=2026-10-01 includes it, and created:2026-10-01..2026-10-26 runs through the end of the 26th. A relative time on its own means "since then": created:-7d is the last seven days.

after:X keeps rows at or after the start of X, before:X rows before the start of X, on the page's time: when a prompt was typed (History, Archive), when a memory file or transcript last changed, a run's last activity. They work beside the date-range picker, which narrows further.

has:

has:field keeps rows where the field is set and not empty (has:session, has:cwd); -has:field keeps rows where it is missing. For numbers it means "not zero" (has:cost).

Regular expressions

/pattern/flags is a JavaScript regular expression, on the main text when bare, on a field after field:. Flags: i (ignore case), m, s, u.

  • A word that starts with / is a regex only when it ends in / plus

    valid flags, so cwd:/src/api is still a path. Quote a value to be sure: cwd:"/tmp/i".

  • Patterns are at most 256 characters. Backreferences (\1) and nested

    quantifiers that can take exponential time, like (a+)+, (.*)* or (\w+\s?)*, are refused with an error.

  • The database cannot run regular expressions, so HarnessLink narrows the

    candidates with everything else in the query (and with the literal text a pattern must contain), then tests the rest, newest first. It tests at most 20,000 rows per search (or about 200 ms); past that the page says Scanned the newest N rows and the counts, fields and chart cover those rows only. Add a field or a date range to search further back.

  • Sorting by relevance does not apply to regex searches; they list newest

    first.

Query page sources

On the Query page, in:history, in:memories, in:runs and in:sessions pick sources; several in: terms combine like any other term, so in:history OR in:runs searches both. The Source filter does the same. A field that one source does not have is simply false there: tokens:>1000 finds only runs, and -tokens:>1000 keeps everything else. Memories have no workspace, and runs and transcripts no device.

Results from every source are listed together, newest first, or by relevance when the query has words: each source ranks its own matches and the list interleaves them, so the first page shows the best of each.

Errors

A query that cannot be read is underlined where the problem is, with a message such as Unknown field "harnes". Fields here: …, OR needs a term after it, Unclosed parenthesis or created: needs a date …. Fix that part and search again.

Limits

A query is at most 2,000 characters, 32 terms and 24 levels of nesting. A query that needs too many separate comparisons is refused as too complex.

Flag notifications