Repository navigation
Releases: Particle-Academy/prism-human-plus-ts
Release list
v0.4.0
Reserve a confirmation verb behind every separator (G-130)
Security fix. Confirmation tools belong to the human: an agent that can call one approves its own proposals. A tool name is chosen by the surface, and the well-formed name check admits ., : and - as well as _, but the reservation looked for the verb only when the whole name was the verb or it followed _. So under TrustPolicy wildcard trust a surface naming its confirmation tool terminal.confirm, sheet:approve, edit-deny, confirm_edit or sheetConfirm handed it to the agent. Upgrade if any surface you attach to can choose its own tool names.
BREAKING CHANGE: more tool names are now reserved for the human and are never offered to the agent, under any policy including wildcard trust and an explicit allowlist entry. The name is split into words at every _, ., : and - and at every camelCase boundary, and is reserved when any word is confirm, reject, accept, approve or deny (case-insensitive). A tool such as approve_change or deny.all that an agent could call before is now refused. Only whole words count, so send_confirmation_email and denylist stay callable.
- The rule is spelled identically in PHP, TypeScript and Python, and leading and trailing invisible characters are still stripped first (G-36).
- The tool-admission conformance corpus is at 0.2.0: 45 rows, including every form above and whole-word controls, recorded by running each language.
v0.3.0
v0.3.0 - Node 22 is the floor
Breaking changes: the declared Node floor moves from 20 to 22. Node 20
reached end of life on 2026-04-30, and engines >=20 told consumers that running
there was sanctioned. Be exact about what this does: npm checks engines and
emits EBADENGINE, but it is a WARNING and the install still proceeds. It is a
refusal only where you set engine-strict=true; pnpm refuses by default. No
source changed, so the code would most likely still run on Node 20 -- what
changed is that it is no longer claimed to.
Tested on Node 22, 24 and 26, on Linux and Windows.
This release adds a README. The package has been on npm with no README
at all, so its page showed nothing you could read before installing it.
Every TypeScript example in it is typechecked against the package's own source
rather than written from reading it. That caught four wrong examples before
release -- an interface method that does not exist, a required field left out,
a wrong argument shape, and a missing import. One claim was not just wrong but
backwards, describing a protection the code does not provide; it now states the
actual limit instead.
v0.2.0
v0.2.0 - Two writers, and what changed since my last turn
This port could not protect a shared surface at all before this release. It had
no revision, no pinning, and no way to ask what a person changed. Both layers
are here now, ported from the reference in one pass, because a change feed
carries the marker the pinning layer establishes and shipping only the second
would have been a different feature wearing the same name.
A tool result may carry an opaque marker for the state it just showed. It is
stored on the attachment and pinned to every later call — reads included,
because this package cannot tell a read from a write and MCP's readOnlyHint
is explicitly a hint the spec says not to trust for security decisions.
A stale call is refused with SurfaceChangedUnderYou, nothing is written, and
the marker is dropped so the agent can read again.
conflictDetection() reports what was OBSERVED: not_observed, unavailable,
minted, enforced. Read minted carefully — it means every call is pinned
and whether the surface enforces the pin is not observable from here.
const changes = await humanPlus.changesSince(owner, attachmentId);
if (!changes.answered()) {
// the surface has no feed; an empty list is NOT evidence of quiet
}
for (const change of changes.deferTo()) {
change.handle; // the surface's own id
change.kind; // created | updated | deleted | moved | unknown
change.actor; // human | agent | other | unknown
}A revision stops an agent overwriting a change it did not know about. It does
nothing about an agent that re-reads, sees current state, decides the surface
has drifted from what it intended, and puts it back over a person's edit —
nothing stale, no pin fires, no error anywhere. That is what this answers.
The empty list is the dangerous value. "Nothing changed" and "I cannot
answer" are the same empty array on the wire, so answered() comes before
changes and nothingChanged() is the only method that means what an empty
list looks like it means.
A change is deferred to unless the surface positively said this agent made
it. On a surface where every write path is an agent tool, that is all of
them — not because they are a person's, but because none can be shown to be the
agent's own.
prism-parity's human-plus-change-feed corpus runs 23 rows through this port,
the PHP reference and the Python port, and compares the bytes. It found a
divergence on its first run, and this port was the one that was right: a
revision of 1.0 was accepted here and rejected by the other two. The
reference changed. What changed here is the boundary — Number.isSafeInteger
rather than Number.isFinite, so a fractional or oversized marker is refused at
the same threshold everywhere rather than accepted as something the other two
spell differently.
v0.1.1
v0.1.1 - A relay URL's trailing slashes are stripped in linear time
Fixed
The SSE + POST relay transport stripped trailing slashes from an invitation's relay URL with a regular expression that takes quadratic time on a long run of slashes. It ran before the checks that refuse a relay URL, so a crafted invitation could hold the event loop for more than a minute before being refused. The slashes are now removed in one pass, and a test holds the refusal of such a URL under a second.
Upgrading is recommended for anything that attaches to invitations it did not issue itself.
Also
- CI checks every direct dependency against the organisation's allowlist, including at publish time.
- The publish check waits up to fifteen minutes for npm to serve a release.
v0.1.0
v0.1.0 - human-in-the-loop for the TypeScript port
@particle-academy/prism-human-plus puts a human in the loop over a stable
JSON/MCP bridge, with the relay injected so it runs with no network.
THE RESERVATION IS THE PRODUCT: confirmation tools belong to the human. An agent
that can call terminal_confirm approves its own proposals, which makes the
whole surface decorative.
A tool name is chosen by the SURFACE, and a single trailing space defeated that
reservation in ALL THREE LANGUAGES at once (G-36). Because they agreed, no
cross-language comparison could see it; it took writing boundary cases and
reading what came back. Names are now normalised against an explicit codepoint
set spelled identically in all three -- deliberately NOT each language's own
trim(), which would have closed the ASCII hole and opened three new Unicode
ones. Since 2026-09-02 the name is validated, not merely normalised.
CONFORMANCE. A tool-admission corpus compares admission decisions across the
three languages. Known open items: G-34 and G-35 concern digest differences for
a tool with no schema, and for an integral float in Python.
This is the first published version, and 0.1.0 is meant literally: a focused,
working slice rather than a 1.0 in disguise.
It is published from CI through npm trusted publishing, with a provenance
attestation you can verify. The 0.0.0 that claimed this name was hand-published
and carries none, which is exactly why it was burned rather than used.