Let out-of-repo plugins write durable session events (the deferred registration surface) #2708
Replies: 1 comment
|
Ran it, so this is no longer a reading of the code. One real session created by a normal turn, three variants, each opened in a fresh
B and C differ by one field. The out-of-repo type alone makes the session unopenable in the UI; the envelope marker alone makes it fine — which is the behaviour So the gap is narrow and precise: a plugin outside this repository can produce B, and has no way to produce C, because The reproduction is a single script if it is useful to you: |
Uh oh!
There was an error while loading. Please reload this page.
CONTRIBUTING.mdpoints bug reports and gaps here rather than at PRs, so this is a report plus a concrete (small) request. It concerns the deferred registration surface named inknown-event-types.ts.Who is asking
I maintain pi2dsh (npm), a compatibility layer that runs Pi ecosystem plugins on DSH unmodified — installed the ordinary way (
dsh plugin add pi2dsh, thendsh plugin add <any-pi-plugin>). It is a single out-of-repo plugin; there is no per-package code in it.The gap
Pi's extension API includes
appendEntry(customType, data)— a plugin records an entry of its own type in the session. Reading the source of the 50 most-downloaded Pi packages, 12 of them call it, so it is not an exotic corner of that API.DSH has the matching concept, and in-repo packages use it: declare the type into
SessionEventMapandsession.append(...).dsh-command-feedbackanddsh-llm-retryboth do exactly that.For an out-of-repo package, that path is closed today, and closed in a way that is invisible until it is too late:
packages/core/session/src/known-event-types.tsis generated fromSessionEventMapmembers declared in this repository, and its own comment says: "Downstream (out-of-repo) plugin events are outside this list by construction; a registration surface for them is deferred until such a consumer exists."packages/session/session-persistence/src/coordinator.tsgates the read path on it:Session.append<T extends SessionEventType>(type, data, ...opts)has no way to setignorable. In this repository only the seed path (Session.create(id, events)) produces an event carrying it.So an out-of-repo plugin that declares its own type and appends it succeeds at write time and produces a session that later refuses to load. Writing is unguarded; the cost appears on the next read, as user data.
What we do instead, and what it costs
pi2dsh keeps those entries in its own file next to the session, keyed by the same session id. That avoids corrupting anything, at a price worth naming: the entries are not in DSH's log, so the host's own conversation view can never show them, they are outside session export/replay, and it is a parallel store living beside a durable log that already solves this problem — which is precisely the kind of thing a compatibility layer should not have to build.
The request
The smallest version, and the one we would use: let
appendmark an eventignorable: true.Nothing else has to change. The skip contract already exists, is documented on
SessionEvent.ignorable, and is covered by tests inpackages/core/session/tests/session.spec.ts. An out-of-repo type would then round-trip: written into the real log, skipped by any build that does not know it, never silently dropped when it matters.A larger version, if you would rather have the vocabulary stay explicit: a namespaced registration (
ctx.sessions.registerEventType('pi2dsh/entry')) that adds to the known set at runtime for this composition, withignorableapplied automatically. We do not need the typed ergonomics — the envelope marker is enough.Why now
The comment defers this "until such a consumer exists". This is that consumer, with a concrete shape: one plugin, one event type, log-only, never on the model-visible surface. Happy to be the guinea pig for whichever design you prefer, and to report back how it behaves in a real composition.
All reactions