Resolve a name, nickname, or partial reference to candidate people.
Call this first whenever the user mentions someone, before asking who they
mean. Returns ranked candidates with a score and match reason. If two or
more candidates are close, the result is flagged `ambiguous` and all are
returned so you can disambiguate with extra context or a clarifying
question. An empty candidate list means no confident match — use
`remember` or `remember_person` to create a new record. Put distinguishing
context in `hints` (`org`, `role`, `relationship`) rather than in `query`.
輸入參數hintslimitquery必填
Assemble a minimal-disclosure context bundle for one person.
Pass `person_id` from `resolve_person`, or `person` (a name or alias) to resolve
inline: an ambiguous name returns the candidates instead of context. Returns
narrow identity fields, active relationships and affiliations, and one ranked
facts/interactions slice capped by `max_items`. Set `include_communication=true`
(or a `purpose` mentioning communication) to include communication traits.
Sensitive and restricted records are never returned by this ordinary tool, and
leave no trace that they exist. `truncated` says the item budget cut the list.
輸入參數personpurposemax_itemsperson_idinclude_communication
Free-text search over stored people for browsing or lookup.
Broader than `resolve_person`: use this to list who is known that matches a
query, rather than to pin down a single identity. Returns ranked candidates.
輸入參數limitquery必填
Search active people and safe interaction summaries by multilingual semantic similarity.
This optional local search requires an explicit `pctx reindex --semantic` first. It never
downloads a model while serving a query and refuses to mix vectors from different model revisions.
輸入參數kindslimitquery必填
Create a new person or update the existing one matching `name`.
Use this to durably record someone the user talks about. `aliases` is a list
of `{value, kind?, lang?, script?}` objects (kinds: nickname, native_script,
transliteration, handle, former_name, other); new aliases are merged into an
existing record. An email address is contact information: store it as a `handle`
alias, never as `name`. Set `summary` to describe who they are, and `is_self=True`
only for the user themselves. Returns the person and whether it was created.
輸入參數name必填sourcealiasesis_selfsummary
Record one thing the user stated about one person, in a single call.
Use this when the user directly tells you something durable — "Alice from Acme
prefers short emails", "Bob is my manager", "I had coffee with Dana today".
`person` is the name as the user said it: it is resolved first, and a new
person is created only when nobody matches. `org`/`role` record an affiliation,
`relationship` records how the user relates to them (e.g. `manager_of`,
`friend_of`, from the user's point of view), and `note` records the statement
as a `fact`, `trait`, or `interaction`. Leave `kind` as `auto` to classify the
note by a fixed keyword rule, or set it explicitly when you know. Pass `occurred_at` when
the statement says an interaction happened earlier ("met Dana last week"); without it such
a note is refused rather than dated today, because that date is what the staleness report
reads.
Identity is never guessed for a write: `status: ambiguous` or `unconfirmed`
returns candidates and records nothing — ask the user, then call again with the
exact canonical name or a unique alias. Everything recorded commits in one
transaction and is audited like the individual tools. For material you
extracted or inferred from a transcript rather than a direct statement, use
`stage_candidates` so the user reviews it first.
輸入參數orgkindnoteroleperson必填sourcepredicateoccurred_atsensitivityrelationshiptrait_category
get_communication_guidance
Return sensitivity-gated signal for client-composed communication advice.
Pass `person_id` from `resolve_person`, or `person` (a name or alias) to resolve inline;
an ambiguous name returns candidates instead of guidance.
輸入參數personperson_idsituation
set_communication_philosophy
Store communication philosophy verbatim while auditing lengths only.
輸入參數text必填
List pull-based reminders, due-dated first and communication notes last.
Filter by `person_id`, or by `person` (a name or alias) resolved inline; omit both for
every person's reminders.
輸入參數personstatusperson_iddue_before
Create a reminder for an existing person.
`kind` is `follow_up` (dated, something to do), `occasion` (a date that recurs), or
`communication_note` (undated guidance surfaced with the person's context).
輸入參數kind必填text必填due_atperson_id必填recurrence
Transition one active reminder to completed.
輸入參數reminder_id必填
Add a normalized-deduplicated alias to an existing person.
`kind` is one of `nickname`, `native_script`, `transliteration`, `handle`, `former_name`, or
`other`, and defaults to `other`. The published schema carries the enum, so an unlisted value
is refused before the alias is built rather than dropped.
輸入參數kindlangvalue必填scriptperson_id必填
Remove one alias, matched by normalized value, from an existing person.
Use this to undo a wrong alias, such as an email address stored as a name. The person and all of
their other names and records stay intact. An alias that is not present returns `removed: false`
and changes nothing; the canonical name is refused with `alias_conflict` (use `rename_person` instead).
輸入參數value必填person_id必填
Correct one alias in place, matched by normalized value; omitted fields stay unchanged.
Keeps the alias's id. A `new_value` that normalizes to the canonical name or to another alias is
refused with `alias_conflict`; an unknown alias is refused with `alias_not_found`. The result lists
which of `value`, `kind`, `lang`, and `script` actually changed.
輸入參數kindlangvalue必填scriptnew_valueperson_id必填
Replace a person's canonical name; the old name stays on as an alias.
Use this to correct a wrong name, such as an email address stored as the name: the old name is kept
as a `former_name` alias, or as a `handle` when it is an email address, so it is still contact
information and imports still match on it. An email address as the new name is refused with
`name_is_email`; a name that already belongs to another person is refused with `name_conflict`.
輸入參數name必填person_id必填
Create a directed relationship between two existing people.
輸入參數type必填labelvalid_toobject_id必填confidencesubject_id必填valid_from
Create an affiliation, resolving an org id or get/creating by name.
輸入參數org必填role必填valid_toperson_id必填confidencevalid_from
Record a time-aware fact about an existing person.
`sensitivity` defaults to `personal`; `sensitive` and `restricted` records are withheld from
ordinary reads. Prefer `remember` for a single statement named by a person's name.
輸入參數value必填valid_toperson_id必填predicate必填confidencevalid_fromsensitivity
Record a subjective observation, separate from disclosed context.
輸入參數text必填person_id必填observed_atsensitivity
Record a derived trait with validated category and provenance.
`category` is one of `communication_style`, `temperament`, `values`, `preference`,
`topics_to_avoid`, or `other`. Cite the observation or interaction it rests on in
`evidence_note` or `evidence_ids` where you can.
輸入參數value必填category必填person_id必填confidencesensitivityevidence_idsevidence_note
Record a concise interaction summary after validating all participants.
輸入參數channelsummary必填occurred_atsensitivityparticipant_ids必填
Correct whitelisted assertion fields in place with before/after audit.
輸入參數fields必填entity_id必填entity_type必填
Close a fact that was true and open its replacement from an effective date.
Use this when a stored value was historically correct and the real-world state changed;
`correct_record` remains the tool for a value that was simply wrong. The old fact keeps its
person, predicate, value, and provenance and is closed the day before `effective_from`; the
replacement inherits the old assertion's original end date, so a bounded claim is never
widened into an open-ended one. Person, predicate, and the replacement's end date cannot be
changed here. Omitting `confidence` or `sensitivity` inherits the old fact's.
Both rows commit together under one logical transaction, or neither commits.
輸入參數fact_id必填new_value必填confidencesensitivityeffective_from必填
Return active relationship structure around one person, capped for bounded disclosure.
Pass `person_id` from `resolve_person`, or `person` (a name or alias) to resolve inline.
輸入參數depthtypespersonperson_id
Return one shortest relationship path, or a structured not-connected result.
輸入參數person_a必填person_b必填max_depth
Create one identified group: a class, cohort, team, department, club, household, or community.
Every call creates a new group; an equal name is never treated as the same group. Use
`find_groups` first and reuse the returned `id` when the user confirms it is the same one.
`organization_id` must name an existing organization and only places the group under it —
it creates no affiliation. `sensitivity` defaults to `personal`; a `sensitive` or
`restricted` group is withheld from ordinary reads together with every membership in it.
輸入參數kind必填name必填sensitivityorganization_id
Record that an existing person held a role in an existing group.
Record only what was stated or confirmed. Leave dates out when they are unknown — never
guess a school year or fill a January 1 date. `temporal_basis` is `unknown` (no dates),
`period` (at least one known bound; a missing bound means unknown, not open-ended), or
`ongoing` (asserted current, no end date); it is inferred from the dates when omitted,
except that `ongoing` must be stated. `role` defaults to `member`. `sensitivity` defaults
to `personal`. Concurrent and historical memberships are all kept.
輸入參數rolegroup_id必填valid_toperson_id必填confidencevalid_fromsensitivitytemporal_basis
Record the last day a historically true membership held.
Use this when someone left a group; the membership's earlier facts are preserved. It refuses
a membership that already records an end (`already_ended`) or an end before its start
(`ends_before_start`). Use `correct_record` with entity type `group_membership` when a
stored membership was simply wrong.
輸入參數ended_on必填membership_id必填
List groups whose normalized name contains `name`, optionally of one `kind`.
Results are candidates, not identity: two groups with the same name stay distinct, so ask
the user which one is meant when several match. Sensitive and restricted groups are never
returned by this ordinary tool. Ordered by normalized name then id; `truncated` reports more.
輸入參數kindnamelimit
Return one group and a bounded page of its recorded memberships.
These are recorded memberships only: sharing a group is context, not proof that members know
each other. Sensitive and restricted groups read as `found: false`, and sensitive or
restricted memberships are omitted without affecting `truncated`.
輸入參數limitgroup_id必填
Return a bounded page of one person's recorded group memberships with each group.
Pass `person_id` from `resolve_person`, or `person` (a name or alias) to resolve inline.
Only ordinary memberships in ordinary groups are returned. An unknown or removed person
returns `found: false`.
輸入參數limitpersonperson_id
explain_shared_connections
Explain how two people are connected through groups they were both recorded in, and when.
Use for shared-background, introduction, and "how do they know each other" questions. Pass
each person as an id (`person_a_id`, `person_b_id`) or a name (`person_a`, `person_b`).
Each connection is one membership of each person in the same group, cited in full. `label`
is `classmates` (both `student` in a `class`) or `teammates` (both `participant` in a
`team`) only when the recorded dates prove a common day (`temporal: overlap`, with the
`overlap` period); otherwise the connection is `shared_context` only. `temporal` is
`disjoint` when the dates exclude a common time and `unknown` when they cannot tell. Sharing a
club, household, or community is not friendship, kinship, or acquaintance; different groups
under one organization, and people linked through a third person, share nothing here.
`direct_relationships` are recorded relationships between the two, kept apart from derived
results. No result means no shared group was found, not that they do not know each other.
Only ordinary groups and memberships are used; `truncated` and `memberships_truncated`
report a partial answer.
輸入參數limitperson_aperson_bperson_a_idperson_b_id
Return people you have not interacted with recently, using ordinary interactions only.
輸入參數limitcategorythreshold_days
Return one person's recent history, newest first, as a bounded chronology.
Pass `person_id` from `resolve_person`, or `person` (a name or alias) to resolve inline.
Entries project durable records — interactions, observations, facts, affiliations,
relationships, and traits — with the stored timestamp each was placed by and which field
that was. Sensitive and restricted records are never returned by this ordinary tool, and a
trait names only evidence that is itself ordinary. An unknown or removed person returns
`found: false` rather than an error.
輸入參數limitpersonperson_id
get_consolidation_context
Return one person's stored facts, traits, observations, and affiliations and how they relate.
Pass `person_id` from `resolve_person`, or `person` (a name or alias) to resolve inline.
Use this before proposing maintenance. `signals` names pairs of records that share a
predicate or category and says how they stand — `duplicate_fact`, `restated_fact`,
`contradictory_fact`, `succeeding_fact`, `duplicate_trait`, `divergent_trait` — comparing
normalized values and inclusive validity periods only. It decides nothing: reading the
evidence and proposing a `correct_record`, a `supersede_fact`, or a `merge_people` for the
user to approve is your job, and several observations supporting one trait are separate
evidence rather than duplicates.
`affiliations` carries the person's stored roles at organizations — employment, education,
membership — with their ids, dates, provenance, and any import receipt, so an incoming CV
claim can be checked against what is already recorded. It has its own page and its own
`affiliations_truncated` flag, and no signal is computed over it: two roles at one
organization may be a promotion, a rehire, or two concurrent posts, and only you can read
which. A `valid_to` of null means the stored assertion set no end, not that the role is
confirmed current, and `created_at` is when the row was written rather than when the role
began. A page that truncates is not evidence that a role is absent.
This read never writes. Sensitive and restricted records are never returned, and a trait
names only evidence that is itself ordinary. Affiliations carry no disclosure level at all,
so this collection adds no sensitivity control and is no route to a protected fact —
background that needs one belongs in a fact. An unknown or removed person returns
`found: false` rather than an error.
輸入參數limitpersonperson_id
Return ordinary birthdays and dated active reminders inside an inclusive upcoming window.
Optionally narrow to one person by `person_id` or by `person` (a name or alias).
輸入參數personperson_idwindow_days
Merge a duplicate person into a primary person atomically.
輸入參數primary_id必填duplicate_id必填
Hard-delete a person or record and redact identifying audit history.
輸入參數scope必填target必填
Extract and atomically stage header-only candidates from a supported source without bodies.
Accepted `source_type` values are `email`, `mbox`, `vcard`, `ics`, `linkedin`, `outlook`,
and `whatsapp`. `self_sender` is an optional chat-export label for the user, such as a
display name or a bare phone number, used to omit the user's own messages.
A `path` import records a receipt for the file it read, so importing that exact file again
reports the existing batch instead of staging a second copy. `forced` says the repeat is
intentional: it stages the same content as a distinct processing session and never weakens
the duplicate rule for later calls. It is also the only way past
`source_previously_redacted` after a hard forget — and for `mbox`, which is read from a
path and cannot be resubmitted as inline content, the only way at all.
輸入參數pathforcedcontentself_sendersource_type必填
Stage agent-extracted people, interactions, records, relationships, groups, and memberships.
Candidate types are `person`, `interaction`, `affiliation`, `fact`, `observation`, `trait`,
`relationship`, `group`, and `group_membership`. Stage every candidate from one source in a single
call. Required fields, beyond `type`; any key the type does not declare is refused:
`person` `ref`, `name` (optional `aliases`); `affiliation` `person_ref`, `org`, `role`; `fact` `person_ref`,
`predicate`, `value`; `observation` `person_ref`, `text`; `trait` `person_ref`, `category`, `value`,
`evidence_note`, `confidence`; `interaction` `summary`, `participant_refs`, `date`; `relationship`
`from_ref`, `to_ref`, `relationship_type`; `group` `ref`, `name`, `kind`; `group_membership` `person_ref`,
`group_ref`. Only `person` and `group` carry a `ref`; the rest point at one.
Staging matches each `person` against stored people and review shows the outcome, so do not resolve
names first. An email address is contact information, never a `name`: put it in `aliases` as
`{"value": <address>, "kind": "handle"}`, which is also what matching reads.
Use this after extracting concise candidates from user-provided notes, meeting transcripts, or other
agent-visible text. Distinguish what was stated (`fact`), what happened in this source (`observation`),
and what you inferred (`trait`, which requires an explicit `confidence` and a concise `evidence_note`).
Relationship candidates carry batch-local `from_ref`/`to_ref` and are ordinary-disclosure only: omit a
relationship the user would consider sensitive or restricted rather than staging it.
References are batch-local; raw notes and source text must not be included in candidate fields. A
request using `observation`, `trait`, or `relationship` is bounded to 500 candidates, a 128-character
`source`, 1 MiB o
輸入參數labelsource必填candidates必填source_kindcontent_digestexternal_source_idextraction_fingerprint
Return staged candidates and statuses for one batch.
The response carries a `batch_digest`. Pass it back to `amend_candidate`,
`withdraw_candidates`, or `commit_import` to have the action refused if somebody else
changed the batch after this read.
A new-person row may carry `possible_matches`: existing people sharing its given name or a
short form of it. They are not matches. Ask the user; to reuse one, pass its id as
`matched_person_id` to `amend_candidate`, otherwise commit to create a new person.
A row with `name_is_email: true` stays unresolved at commit: ask the user for the person's name and
amend `name` to it, keeping the address as a `handle` alias.
輸入參數batch_id必填
Correct one staged candidate before it is committed, returning the revised batch.
The patch replaces the named fields and leaves the rest alone. Nothing is committed:
confirming an amendment is not acceptance of the batch.
輸入參數patch必填batch_id必填candidate_id必填expected_batch_digest
Drop staged candidates from consideration, returning the revised batch.
A withdrawn candidate stays listed as `rejected` so the user can see what was dropped, and
is never committed. Nothing else about the batch changes.
輸入參數batch_id必填candidate_ids必填expected_batch_digest
Commit accepted people and resolvable interactions idempotently.
The receipt is the verification: an id in `committed_ids` is stored. Read records back only for ids
in `unresolved_ids` or `skipped_ids`, or when the user asks to see them. Accepting a withdrawn
candidate refuses the whole commit. Supplying the
`expected_batch_digest` from the review the user approved refuses it if the batch moved
after they saw it.
輸入參數batch_id必填accepted_ids必填expected_batch_digest