Skip to main content
A grounded copilot does not act on what the model is confident about. It acts on what the graph can support. This build wires the read surfaces into a gate the copilot passes before every action: gather context, confirm it is backed by real sources, confirm it is sufficient for the specific task, then act or repair.
gather ──▶ trace (is it grounded?) ──▶ check (is it sufficient?) ──▶ act or repair
Prerequisite: a client. The gather and check steps below only read; the repair step writes through a delta you control.

Set up the client

import { createPenumbra } from "@penumbra-systems/platform";

const pb = createPenumbra({ apiKey: process.env.PENUMBRA_API_KEY });

The loop

1

Gather context

Pull what the copilot needs from the graph, then synthesize recalled memory into a briefing it can reason over.
const hits = await pb.search("Acme renewal status and open risks", {
  include: ["entities", "chunks"],
  limit: 10,
});

const briefing = await pb.memory.synthesize({
  query: "what should be known before contacting Acme?",
  as: { type: "SituationBriefing", purpose: "agent-handoff" },
});
2

Check evidence coverage

Confirm the briefing is grounded in stored sources, not inferred. Pass it straight to pb.dq.trace.
const trace = await pb.dq.trace(briefing);

if (!trace.grounded) {
  console.log("Unsupported claims:", trace.unsupported);
}
Each claim comes back marked supported (backed by a stored source), inferred (related but not directly sourced), or unsupported. A grounded copilot treats anything below supported as a gap, not a fact.
3

Check fitness for the purpose

Coverage is not enough. Ask whether the subject is sufficient for the specific thing the copilot is about to do. The verdict is a disposition plus findings, never a score.
const verdict = await pb.dq.check({
  subject: { type: "Account", id: "acme" },
  purpose: "send a renewal proposal",
});
The same subject can be fit for one purpose and unfit for another. Tie the check to the action, not to the record.
4

Act or repair

Let the copilot proceed only when the briefing is grounded and the subject is fit. Otherwise turn the findings into remediation steps, fill the gaps, and re-run the gate.
if (verdict.safeToAct && trace.grounded) {
  // act
} else {
  console.log(verdict.explain());
  const plan = pb.dq.repair(verdict); // what to hydrate, capture, or extract
  // run the steps via pb.*, then re-run trace + check before acting
}

Stage repairs through a delta

When repair says the copilot needs to capture or extract context first, route those writes through a delta so nothing lands in the graph unreviewed. Stage with apply: false, preview the diff with plan, and commit only when it looks right.
const delta = await pb.deltas.create({ name: "fill Acme renewal gaps" });

await pb.deltas.addEntities(delta.id, [
  {
    type: "Risk",
    name: "Pricing objection from procurement",
    properties: { description: "Procurement flagged the renewal price uplift." },
  },
]);

const preview = await pb.deltas.plan(delta.id); // see the diff before committing
await pb.deltas.apply(delta.id);                // commit
If a committed repair turns out wrong, pb.deltas.revertPreview(id) shows what reverting would undo and pb.deltas.revert(id) rolls it back.
After the repair commits, re-run pb.dq.trace and pb.dq.check before the copilot acts. The gate is the loop, not a one-time setup.

Why this holds

A copilot acting on ungrounded or insufficient context fails quietly. Putting a trace and a check in front of every action turns “the model was confident” into “the graph was sufficient and sourced” — a claim you can audit and a gate you can repair through.

Decision quality

Dispositions, findings, and repair.

Memory

Remember, recall, and synthesize.