| searchA | Route to the most relevant ATOMS in the trusted knowledge base (David's saved
opinions + tracked artifacts). This is a ROUTER, not an answer: each hit is a thin
card — matched-chunk snippet + a pointer (source_url / raw_ref / atom_id) + ranks —
NOT a statement of what the source says. To assert what a source actually claims,
call open(atom_id) and read its raw text. That split is the whole trust model. RETURNS {hits, notices, insights, trace, frontier_atoms} — and the non-hit keys are
read DIFFERENT ways:
• notices — finished sentences about what your QUERY did (a filter that matched
nothing, a handle that resolved to nobody or to two people, results truncated).
Surface these to the user when the list is non-empty; they are written to be
repeated as-is. [] on a healthy query, which is the normal case.
• insights — VALUES about the evidence: authors/sources/topics counts,
date_span, body_state, saved_vs_crawled, corpus_newest, filter_cost
(what each filter cost you), and resolved_who when you passed who=. These
describe hits ONLY. On a default call saved_vs_crawled therefore lists no
crawled atoms — they are in frontier_atoms, not missing.
• trace — VALUES about what the ENGINE did: ran (which arms actually ran),
score_scale (the units score is in), candidates/ranked/showing,
cutoff, fts_query, filters as applied.
• frontier_atoms — a SECOND, separately ranked list: atoms found by the user's
standing keyword queries rather than saved or written by anyone they follow. Present
only on a default call (see entry_mode). Same card shape as hits, capped at 8 and
floored at a fraction of its own top score, with floor.dropped saying how many the
floor removed. Offer it as "and from the frontier crawl…", never merged into hits:
its scores are ranked against other frontier atoms and mean nothing next to theirs. frontier_atoms is NOT the frontier key you may also see here. That one is the
Frontier QUEUE's push notice — staged candidates not yet in the KB — and it appears at
most once a session. These are atoms already in the KB that matched THIS query.
insights and trace are for your reasoning — do not recite them to the user.
They are bare values on purpose. Use them to decide what to do next (open something,
re-query, drop a filter, warn about a lopsided result); say the CONCLUSION in your own
words, never the fields. Only notices is written to be read out.
score is not comparable across calls unless trace.score_scale matches. It is a raw
cosine under semantic (and under a hybrid run that dropped its keyword arm), a
reciprocal rank under bm25, and a fused rank sum under a true hybrid run. 0.03 in one
scale can outrank 0.7 in another. A known bias rides the cosine: a longer document
max-pools higher for having more chunks to draw from, so weigh a long hit's lead over a
short one as smaller than it looks.
Retrieval: an optional tag/kind/source/author pre-filter, then a BM25 arm and a
semantic arm, fused by rank. Ranking is pure relevance — there is no trust re-rank.
mode="hybrid" often runs only the semantic arm: a conceptual query with no literal
token (most natural-language questions over three words) gives BM25 a weight of 0 and
the keyword arm is skipped. trace.ran says which arms really ran; do not assume both. Read body_state before quoting a snippet. It says how much of the source we actually
stored: "complete" (the whole body), "partial" (knowingly short of it — a paywall teaser,
a truncated feed entry), "absent" (no body, the card is all there is), or "pending" (not
yet determined). On "partial" or "absent", do not present the text as the full thing —
say what you have, and follow source_url for the rest. body_basis says how that was
decided: "observed" (we saw the boundary), "stated" (the source declared it), "assumed". Read when_precision before reporting a date, especially under date_from/date_to.
when_ts always LOOKS like a day, and for two values it is not one:
• "year" — only the YEAR is known (common for papers); when_ts is that Jan 1 as a
FLOOR, not a real day. Such a hit is included whenever its year OVERLAPS your
window, deliberately — a wrongly-included atom you can see and discard, a wrongly
excluded one you cannot. Caveat it to the user ("published sometime in 2025"),
never as a confirmed date match.
• "push" — GitHub's LAST-PUSH date, NOT a publication date. A repo matching "after
May" was ACTIVE then and may have been created years earlier. A different KIND of
date, not a coarser one, and the easiest thing here to misreport.
Atoms with NO recorded date are excluded by either bound, and a notice says how many. Each hit also carries payload — whatever extras that atom's SOURCE had, returned
verbatim. It is NOT a fixed schema and it is NOT filterable: GitHub atoms carry
stars/code_language, X atoms like_count/is_thread, papers citationCount/venue. Read the
keys that are there; never assume a key exists because another hit had it. |
| openA | Follow an atom's pointer and return its REAL raw snapshot text + live source_url.
Call this before citing or asserting anything an atom "says" — search only
routes you to the atom; THIS gives you the ground truth to reason from. Returns
{atom_id, source_url, raw, description, body_state, body_basis, payload, …}; an unknown
id returns {error: "not found"}. The pre-citation check: raw is only as complete as body_state says. "complete" means
you have the whole body. "partial" means you have a knowing fragment — a paywall teaser
or a truncated entry — so quoting it as the full article invents a citation; attribute
what you have, and send the reader to source_url. "absent" means there is no body at
all. body_basis says how that was determined (observed / stated / assumed). payload
holds that source's own extras verbatim, and its keys differ per source. kb must be whatever the hit card carried. An atom id is scoped to ONE knowledge base —
the same tweet in two people's stores is one id in each — so opening a foreign id without
its kb either finds nothing or hands you your own copy of the same source.
|
| aggregateA | A state-of-play skeleton over a SCOPE of the KB — counts by kind/source, trust
coverage, topic/entity distribution, and the most-recent atom DESCRIPTIONS (mechanical,
so safe to read without opening). Use it to draft a dossier or "what do I have on X",
THEN open() the pivotal atoms to ground each claim in raw text. Scope is optional:
omit everything for the whole store, or filter by tags / what_kind / source_type /
who_id / date_from / date_to. This takes IDs, not handles. To scope to a person, call search(who="@handle")
first and pass its insights.resolved_who[].who_ids here. |
| oracleA | Choose who to trust: turn the people you already curate (follows, Lists, bookmarks,
subscriptions, likes) into your Oracles — the sources the KB deep-ingests and roots
trust on. Runs entirely in chat. NOT the setup tool. onboard readies the machine (keys, consent, the first curation pull);
this decides WHO is in. Reach for this when the user asks who to trust, wants to see or
change their people, or asks whether their sources are current. FLOW: call action='screen' → read the ranked candidates to the user (the PRE-TICKED ones
are people you've corroborated with ≥2 distinct signals — your default-yes set; the rest are
shown unchecked; non-persons are demoted to the end, never hidden) → ask which to keep →
call action='confirm' with the kept canonical_ids → then, before action='ingest', ask
the user how far back to pull and pass their answer as x_lookback. Why that question is not optional: the default is only ~6 months, so a user who wanted
their Oracle's whole visible history gets a fraction of it and is never told. (It is no
longer a COST question — X reads are free since the cutover — it is a completeness one,
which is the same reason to ask and a different reason to give.) action='screen' returns the exact presets and defaults under lookback_options —
offer those, don't invent your own. The web archive is FREE and already pulls in full, so
ask about it (web_lookback) only if the user wants it NARROWED. To add someone NOT in the list, pass their X @handle or Substack URL in add_handles —
they're resolved and added on the spot. action:
• "screen" (default) — the ranked candidate payload. Each candidate carries its
reflected signal ("you follow · subscribe · bookmarked 12×"), pre_ticked
/ shown_by_default / is_person flags, distinct_signals, and its
canonical_id (pass these to confirm). classify.ran=False means the kind
classifier degraded open (LLM unavailable) — everyone stays person-eligible.
• "confirm" — commit Oracle picks. canonical_ids = the ones the user kept (verbatim from
screen); add_handles = raw X handles / Substack URLs to add (resolved-at-
confirm). Idempotent. Returns {confirmed, unresolved, unknown, total_oracles}
— surface unresolved (handles a lookup couldn't find) to the user.
• "ingest" — deep-ingest confirmed Oracles into the atom-KB. For each (all confirmed, or
just the canonical_ids you pass) run discovery — which mines their trusted
blog/Substack for their OTHER profiles — then ingest each trusted personal
profile as atoms attributed to the Oracle; an org link becomes an affiliation;
an ambiguous one is left for review. Returns a per-source outcome report,
with the windows actually pulled under lookback — read those back.
• "candidates" — the OTHER half of the promotion decision. screen ranks people by how
hard the USER vouched for them (distinct curation signals); this ranks the
same people by what they actually write. Pass query to ask a topic
question ("who writes about agent memory"); omit it to list who is there.
Evidence comes from TWO stores and every row names which, in basis:
· "probed" — a light sample of their own timeline (~25 posts). UNVETTED:
nobody vouched for this text, only for the person being worth a look.
Never cite it as knowledge-base content or quote it as fact.
· "saved" — a post the USER saved, so it IS a knowledge-base atom and is
citable (open it first). Usually exactly ONE post, and it is a positive
example rather than a sample of their output: it matched partly BECAUSE
the user kept it. Good evidence for "worth reading more of", weak
evidence for "this is what they mostly write about".
Scores are comparable only WITHIN a basis; the two are interleaved by rank,
never added. Read back no_local_material — a candidate absent because
nothing of theirs is stored looks exactly like one whose writing did not
match, and those need opposite actions.
Is my stuff still current? You do not ask — screen answers it unasked, under
oracle_freshness: per-Oracle, per-source last_pulled_at / hours_overdue /
never_refreshed / breaker_open, worst first. If needs_attention is true, read its
note out — that flag means either nothing has ever opted in to refreshing, or the cycle
has stretched past twice its target. Both are silent failures otherwise. A model_routing key, when present, lists models that are dead (no OpenRouter
provider survives the deny-list — the stage using them cannot run) or fragile (one
surviving provider — one withdrawal from dead). Read it out: it is absent whenever
everything is routable, so its presence IS the news. |
| add_oracleA | Add a person to your knowledge base as an Oracle — a trusted source OPYT
deep-ingests (their X timeline + Substack/blog archive + GitHub) and roots trust on. This
is the atom-KB "add a person" — the only one: the old vault-era add_person tool, which
wrote the legacy vault and couldn't admit a Substack/blog-rooted person, is retired. SAFE, TWO-PHASE — always preview before you ingest:
• FIRST call with confirm=False (the default) → a PREVIEW. It resolves reference and
returns who they are (name, bio, followers) — or, if already known, their roster entry
— and writes NOTHING. Read it back to the user to confirm it's the right person. If the
reference doesn't resolve you get unresolved and there's nothing to confirm.
• THEN, once the user agrees, call again with confirm=True → runs the full ingest and
writes to the store. Never call confirm=True without showing the preview first —
the guard is against ingesting the WRONG PERSON, which no later call undoes. reference is polymorphic: an X @handle ("@karpathy"), a Substack/blog/site URL
("https://simonwillison.net"), or a canonical_id from oracle(action='screen') (to promote
a below-the-cut candidate). To add someone by NAME, YOU resolve the name → their @handle or
URL first (your own knowledge / a web search), then pass that — there is no name-search
endpoint.
TWO windows, asked separately, because they answer to different constraints:
• x_lookback — "6mo" (default) / "1yr" / "2yr". Hard-capped at 2 years whatever you
pass. ASK the user before a confirm=True; the ~6-month default silently leaves most of
a prolific account's history out.
For someone ALREADY on the roster, "since_last" pulls only what is new since their
last pull — the cheap top-up, roughly one request for a few days' gap. Reach for it
when the user says "update" or "catch up", not "add".
• web_lookback — "1yr" / "2yr" / "5yr" / "all" (default). A durable archive, so it
already pulls everything. Pass it only to NARROW.
A single shared window would be wrong for one of the two by construction — it either
over-pulls X or truncates the archive. The result echoes the windows that actually ran
(including the X clamp) under lookback — TELL the user how far back you pulled. Reading the result — do not report ingested as if it were the whole story:
• ingested counts sources that actually ingested; blocked counts sources where the
host stopped us (Cloudflare, a truncated archive). A blocked source wrote nothing and
is retried on the next run — say so plainly ("their Substack was blocked, nothing was
saved, it'll retry") rather than implying it worked. errors is the "something is
wrong, worth a look" bucket.
• atoms_added vs dispatched: lookback/limit bound posts ATTEMPTED, not atoms
saved, so these two diverge whenever posts are paywalled or fail the quality gate. If
atoms_added is much smaller, tell the user the number they actually got.
• producer_failed > 0 means posts vanished mid-run — mention it; nothing else records it. The result may ask you to do something — check for followup. A confirm=True result
carries one, and acting on it is how this tool finds a person's blog / YouTube / podcasts
at all. OPYT's four probes are deterministic (X bio, Substack convention, GitHub, Semantic
Scholar); the open-web step is YOURS, because you have web search and OPYT would otherwise
pay a second model for a worse version of it.
So: run the search it describes, then call this tool AGAIN with the same reference,
confirm=True, and the URLs in extra_source_urls. Send everything plausible — you do NOT
need to verify ownership, because the trust graph re-checks every URL and rejects what it
cannot corroborate. A URL you drop is invisible; a URL it rejects lands in needs_review. |
| onboardA | Set up OPYT on this machine. Call this first on a fresh install, and any time
setup looks incomplete. Idempotent and re-entrant — it recomputes where you are from
disk on every call, so calling it twice never repeats a finished step. It runs in up to three calls, because two steps wait on a human: OpenRouter — a browser tab opens; click Approve. Nothing to paste. Your browser session — reads your own logged-in X cookies, on this machine
only. This is the ONLY way OPYT reads X: there is no API key and no third party.
If no browser holds an X session, Opyt can open one for you to log into
(guided_login=true). A couple of browsers still trip a native consent prompt
(Full Disk Access for Safari, Keychain for Arc); when one applies, this tool warns
you before it appears. Consent — one question, two commitments, and you may answer them separately.
Arguments are decisions, never credentials:
• browser_profile — which Chrome profile holds your X session, when several are
logged in. The answer is remembered in settings.yaml.
• guided_login — accept the offer to have Opyt open a browser window you log
into, when no browser on this machine has an X session.
• consent — one of both | backlog | refresh | none.
• skip_github — GitHub is optional and skipped by default. Then call oracle to choose who to trust. |
| frontierA | The FRONTIER queue: recent artifacts (papers and repos — arXiv preprints, GitHub
repos, and published literature across every discipline via OpenAlex) that the user's
own standing queries pulled from the outside world, RANKED by how much they deserve
attention right now. Read-only and free. Use it when the user asks what is new, what the
frontier found, or what they have not looked at yet. What the ranking is. Recomputed on every call from checkable facts, never stored: how many
standing queries found the same artifact (the strongest signal — independent convergence),
how many regions of the user's KB asked for those queries, how recent it is, how
substantial it is for its kind (stars for a repo, abstract length for a paper — never
compared across sources), minus how often it has already been shown. One artifact is ONE card even when several sources staged it under different ids (the same
preprint reached by DOI and by its /abs/ page, a paper deposited twice). Such a card
carries duplicate_of naming the ids it stands for — it is a merge, so no signal is lost
and total counts artifacts rather than rows. Nothing is ever filtered out. Every term demotes; none excludes. Each candidate carries a
state — "new", "seen" (shown before), "dismissed" (the user said stop), or stage 3's
verdict — and a dismissed one still comes back, ranked last and labelled. Report a
dismissed item as dismissed; do not hide it from the user and do not re-pitch it. Calling it again advances the queue. This tool records what it showed you, and being shown
demotes — so a second call surfaces the NEXT batch rather than re-pitching the same head.
There is no cursor to pass. remaining > 0 means there are more below the cut; call again,
or raise limit. Nothing gets stranded: an unseen candidate carries no penalty at all, so
it outranks everything already shown. There is no save step here, and you should not invent one. Admission into the knowledge
base is Frontier stage 3's job and it is AUTONOMOUS — it runs on its own schedule, with no
approval step and nothing for you to call. A candidate's state tells you what stage 3 has
already decided: "materialized" means it is in the knowledge base, "rejected" means the
fetch mechanically failed. "rejected" is NEVER a quality judgement — stage 3 has no
judge. Report it as a fetch failure, never as "not good enough".
Do not tell the user YOU added or kept anything, and do not offer to: nothing you do here
admits an artifact, and as of 2026-08-13 there is no tool anywhere that admits one on
request — the save_paper / save_repo vault writers were deleted. Stage 3 is the only
admission path and it runs on its own. Params: limit (default 20), dismiss (list of candidate_ids the user explicitly wants
stopped — pass ONLY on an explicit request, never on inference), include_dismissed
(default True; passing False hides them and reports how many it hid). |
| hopperA | SAVE something into the knowledge base — a link the user wants to KEEP. Hand it any URL
and it works out what the thing is and routes it to the right ingester: a research paper,
a GitHub repo, a Substack post, a plain article or blog post, or a single X post. This is
the ONLY manual "keep this" path; there is no separate save-a-paper or save-a-repo tool. Reach for this whenever the user says keep / save / add / remember / "put this in OPYT"
about a link — including a link YOU just surfaced from a web search. It persists a link
into the trusted corpus so the knowledge-base search tool can route to it later. TWO-PHASE, and the first phase is free or near-free:
• confirm=False (the default) → a PREVIEW. It reports which adapter the reference routes
to, WHY it routed there, the atom id, whether the KB already has it, and what a confirm
would spend. It NEVER writes. It fetches nothing for an article, paper, repo or
Substack post — you can already read those yourself, so describe them to the user in
your own words alongside the routing.
The X exception: for an x.com status link the preview reads the post (~$0.00015)
and returns a description. You cannot fetch x.com, and x:2086520133909168332 is
unverifiable by a human — so read that description back before confirming; it is the
only way the user can catch a wrong link. If unreadable comes back instead, the post
is deleted / protected / keyless: say so and do NOT confirm.
Paywalls are your job, not the preview's. This tool stores PUBLIC content only —
a paywalled Substack post is skipped by the adapter and comes back "failed". It reads
the same cookie-less public endpoint you do, so it cannot see past a wall you hit
either. If the page you read was a subscriber teaser, say so BEFORE confirming instead
of spending a round trip to be told no.
• confirm=True → runs the ingest. This spends: a metered embedding always, plus a content
gate for articles and ~$0.003 for an X post's thread. Show the preview first — a wrong
route is SILENT (a paper filed as a blog post never errors, it just sits wrong). Skip straight to confirm=True only when the user has already said "yes, save it" about
THAT specific link. already_present: true in a preview means a confirm is a no-op — say so and don't spend.
Repeat calls are idempotent: an unchanged item is never re-fetched or re-embedded.
What it will not do — do not ask it to, and do not work around it:
• It never adds a PERSON to the tracked roster. Saving someone's article does not start
following them. add_oracle is the only way in, and it asks the user first.
• It never writes vault notes. Atoms only.
• It never guesses. A reference that is not a URL comes back unroutable with nothing
written, rather than being filed somewhere plausible. Every saved atom is stamped entry_mode='user-saved' — the same mark an X bookmark gets,
because both mean the user personally saved it. That is load-bearing downstream: hand-saved
items steer the Frontier's standing research queries. |
| sittingA | Read one topic of the user's knowledge base end to end, in publication order — as
standing research queries, a briefing, a trajectory, or a search for what contradicts or
is missing from it. Reach for this when the user wants to know what their saved material actually SAYS about a
subject over time — "what have I been collecting on agentic payments", "how has the
thinking on X moved", "read everything I've saved about Y", "what's in my blind spots". It
is the opposite of the search tool: search finds the few best-matching items, this
assembles EVERY item on a topic and reads the whole set in date order. Search answers
"where is it"; this answers "what happened". Do not reach for it to look something up, to answer a factual question, or to find a
specific post — that is the knowledge-base search tool, and it is free and instant. This
assembles a large context and, on read, makes a model call (the other actions do not). The five actions: • preview (FREE, the default) — name a topic and find out what is actually there. It
grows the region and reports its size, the stretch of time it covers, how many people
wrote it, a few of the items by name, and anything about its shape that would make a
read disappointing. It calls no model. Costs one metered embedding for a typed phrase
(a fraction of a cent) and nothing else.
This is not a permission step. It is here because a phrase can resolve to four items or
to two hundred and the user cannot tell which in advance — the size is a property of
what they have saved, not of the words they typed. Skip it whenever the user has
already said to go ahead.
A preview alone queues nothing — consumption subscribes a region, construction does
not. Nothing reads it, and nothing spends, until read or lens actually consumes it. • read (SPENDS) — reads the assembled region with a model. A topic too big for one
sitting is read in PARTS, oldest stretch first; each read carries forward the claims
every earlier part established and is asked to confirm, revise or refute them. Two lenses, pick with lens:
- queries (default) — emits standing research queries from the material. Those then
run on a schedule against papers, repos and datasets, catching what gets published
NEXT in that thread.
- claims — extracts 8-15 falsifiable claims the material actually makes, each one
naming specifics (systems, numbers, dates), citing every atom that supports it, and
stating what observation would prove it WRONG. Use this when the user wants to know
what their saved material actually establishes, not what to watch next.
Pass a sitting_id from a preview, or pass query to build and read in one step. The
two lenses spend and read INDEPENDENTLY — reading a region for claims does not use up
or block its queries read, and the reverse holds too. • render (FREE) — hand back a region that was already built, as the document a reader
would see. Nothing is re-grown and nothing is re-read. • watchlist (FREE) — the standing questions currently being watched on the user's
behalf, with how often each runs, how many times it has come up, and whether the user
typed it or a read of their material proposed it. Pass a sitting_id or query to see
one region's; pass neither for everything.
SHOW THIS ONLY WHEN ASKED — "what am I watching", "show my watchlist", "did anything
change". Never volunteer it at the start of a session or alongside unrelated work.
add puts questions the user names onto the list; those never decay and are removed
only by drop. drop retires a question EVERYWHERE — a question two regions both
watch is retired for both, because the list is one list of questions, not a copy per
region. Say so before dropping something the user did not name precisely. • lens (SPENDS only on material never lensed before) — hand back an instruction plus
a document, and read them YOURSELF, right here in this conversation, to answer the
user directly. This is how you answer a question ABOUT the material rather than
generating queries against it.
The document is NOT the region's raw text. A topic read across several sittings is
summarised one stretch at a time, and what comes back is those summaries labelled with
the dates they cover — so you are joining stretches, not re-reading everything. Each
stretch is summarised once ever, so asking the same lens again is free, and asking a
DIFFERENT question of the same lens is free too. Only material that has never been
lensed this way costs anything. Pass lens to pick which reading:
- briefing — what this material actually says, as knowledge, not a table of contents.
- trajectory — how the thinking on this topic MOVED over time: what changed,
reversed, or got abandoned.
- disconfirmation — what in this material would UNDERMINE a belief. Pass claim
with the belief being tested; without one, it red-teams the material's own apparent
thesis.
- gaps — answer a question using only this material, and if nothing here answers
it, the CLOSEST it comes and why that falls short (never a bare "nothing here").
Pass claim as the question.
- sprouts — everything no sitting has ever read: true orphans, unread regions,
fracture leftovers. This is what "what's in my blind spots" means. Needs no
sitting_id, query, or atom_ids — it is not about one topic.
Like read, pass a sitting_id from a preview or a query/atom_ids to build one in
the same call (sprouts needs neither). The answer YOU give is never written anywhere
— no queries, no table, no record of it — because it is about the topic as it stands
today and would be wrong the moment anything is added. This is a conversation, not a
rail. What comes back from a read, lens queries: a consensus — how the conversation moved,
what reversed, what is unresolved — plus the queries it emitted. Show the user the
consensus. It is the part written for a human. What comes back from a read, lens claims: a claims list, each one {claim, falsified_by, atom_ids}. Show the user the claims themselves — falsified_by is what lets
them decide whether to believe one, so surface it alongside the claim rather than dropping
it. What comes back from a lens: instruction and document. Read document following
instruction and write the answer yourself — there is no second call to make, and nothing
here reads the document for you. Read the warnings in a preview back to the user. They say when a region is a poor fit
for the question — a region spanning three days has no arc to find, a region that is 85%
one author generates queries pointing back at that author's own work. Pass the lens you
intend to use to preview and the warnings are specific to it (a short span breaks
trajectory and barely touches briefing). They are advisory: nothing here refuses to
read, because whether the region is right depends on what the user is asking and only they
know that. A region is read once per lens (by read). A second read of the same sitting_id with
the SAME lens is refused as already read — it would be the same input for the same money.
The two read lenses do not share this guard: a region read for queries can still be
read for claims, and the reverse. A region is re-read (same lens) when it has GAINED
enough new material to be worth redoing, and the rail decides that on its own; to force it,
preview the same phrase again, which grows a fresh region over the corpus as it is NOW.
lens (the action) has no such limit — each stretch is summarised once and reused after
that, so reading the same region's briefing twice costs nothing the second time. |
| shareA | SHARE this knowledge base with someone — returns a link to send them. Reach for this when the user says share my KB / send my knowledge base to X / let X search
what I've read. It hands back one link. The person who opens it can install Opyt if they
need to, and after that their assistant can search this corpus, with every result
attributed here. TWO-PHASE, and the preview is the consent step, so run it first:
• confirm=False (the default) → a PREVIEW. It publishes nothing, mints nothing and
hands nobody anything; on an install that has already shared it asks the service
whether an export has landed, and that is the only thing it sends. It
reports WHAT would be shared — how many atoms, from which sources and authors, over
what date span — and what sharing means. READ THAT BACK to the user before confirming.
Sharing is the WHOLE knowledge base, standing, not a slice and not a snapshot: there
is no way to share part of it, anyone given a link keeps access until it is revoked,
and the copy refreshes itself when people read it.
• confirm=True → registers this install with the service if it is not registered yet,
starts the first upload, mints a one-time invite, and returns the link. Skip straight to confirm=True only when the user has already said yes to sharing THIS
knowledge base, having been told what is in it. The first share uploads the whole corpus, which runs in the background and usually takes a
minute or two. The link works immediately: a reader who accepts inside that window is
registered normally, and it is their first SEARCH that comes back saying the copy is
still arriving. The next one works. Later shares are instant. WHAT THIS IS NOT. It does not publish anything on the open web — the service serves only
people holding a link, one reader token per link, revocable. It does not send anything to
the person for you: hand the user the link and let them send it. |
| acceptA | ACCEPT an invitation to search somebody else's knowledge base. Reach for this the moment the user pastes an Opyt invite link or a grant code, or says
someone shared their knowledge base with them. One call and it is done: from then on the
search / open / aggregate tools take kb='<name>' and read that person's corpus, with
every result attributed to them. SINGLE-PHASE on purpose — there is no preview. A grant code buys exactly one reader token
and then dies, so checking it would spend it, and pasting an invite is already the yes. Pass whatever the user gave you: the whole link, the fragment, or the bare code. The code
is found inside any of them. |
| unshareA | STOP sharing — either with ONE person, or with everybody. reader picks which. READ THIS BEFORE CALLING. The two scopes are not the same act and only one of them is
expensive to undo:
• reader="Leo" → Leo loses access. Everyone else keeps reading, the served copy
stays, and you can invite Leo again with share whenever you like.
• reader omitted → EVERY reader is cut off AND the served copy is deleted. Every
invite ever sent stops working, so sharing again means re-inviting everyone by hand. So when the user names a person — "stop sharing with Leo", "cut Leo off", "revoke Leo" —
reader is REQUIRED. Omitting it there does something much larger than what was asked,
and the preview is where you catch that: it always says which of the two scopes it is
about, in its first sentence. Reach for the whole-knowledge-base form when the user says stop sharing / unshare / take
my knowledge base down / revoke everyone. That form is one act and not two, because a
person who says "stop sharing my KB" means both halves and will not say it twice. |