@mgiles/perk 3.2.0 → 3.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (202) hide show
  1. package/README.md +5 -0
  2. package/extension/authoring/gist/draft.ts +198 -0
  3. package/extension/authoring/gist/prose.ts +46 -0
  4. package/extension/authoring/gist/review.ts +133 -0
  5. package/extension/authoring/gist/save.ts +118 -0
  6. package/extension/authoring/objective/draft.ts +345 -0
  7. package/extension/{factories/objectiveDreamReport.ts → authoring/objective/dreamReportGate.ts} +74 -131
  8. package/extension/authoring/objective/planning.ts +124 -0
  9. package/extension/authoring/objective/prose.ts +103 -0
  10. package/extension/authoring/objective/review.ts +128 -0
  11. package/extension/authoring/objective/save.ts +224 -0
  12. package/extension/authoring/plan/draft.ts +84 -0
  13. package/extension/authoring/plan/prose.ts +41 -0
  14. package/extension/authoring/plan/review.ts +269 -0
  15. package/extension/authoring/plan/save.ts +256 -0
  16. package/extension/authoring/plan/source.ts +82 -0
  17. package/extension/authoring/refinement/context.ts +468 -0
  18. package/extension/authoring/refinement/draft.ts +261 -0
  19. package/extension/authoring/refinement/prose.ts +79 -0
  20. package/extension/authoring/refinement/review.ts +111 -0
  21. package/extension/authoring/refinement/save.ts +119 -0
  22. package/extension/authoring/review/approvalGate.ts +34 -0
  23. package/extension/authoring/review/draftContext.ts +68 -0
  24. package/extension/codeReview/automated.ts +352 -0
  25. package/extension/codeReview/submission.ts +229 -0
  26. package/extension/delivery/address.ts +295 -0
  27. package/extension/delivery/ci.ts +355 -0
  28. package/extension/delivery/commitCompact.ts +93 -0
  29. package/extension/delivery/conflictResolution.ts +247 -0
  30. package/extension/delivery/ready.ts +193 -0
  31. package/extension/delivery/stackConflict.ts +361 -0
  32. package/extension/delivery/stackObjective.ts +16 -0
  33. package/extension/delivery/stackReconcile.ts +165 -0
  34. package/extension/delivery/submit.ts +171 -0
  35. package/extension/index.ts +365 -380
  36. package/extension/learning/analystWave.ts +324 -0
  37. package/extension/learning/audit.ts +667 -0
  38. package/extension/learning/capture.ts +92 -0
  39. package/extension/learning/containment.ts +104 -0
  40. package/extension/{waves/dreamWave.ts → learning/dream.ts} +112 -94
  41. package/extension/learning/dreamAnalysis.ts +435 -0
  42. package/extension/{waves/dreamReducerWave.ts → learning/dreamReducer.ts} +46 -41
  43. package/extension/{waves → learning}/dreamReport.ts +35 -31
  44. package/extension/learning/harvest.ts +491 -0
  45. package/extension/learning/prose.ts +66 -0
  46. package/extension/learning/routing.ts +79 -0
  47. package/extension/pi/v1/bashScanTimeout.ts +64 -0
  48. package/extension/{doors/prReview.ts → pi/v1/codeReview/automated.ts} +215 -311
  49. package/extension/{doors/prReviewBrowser.ts → pi/v1/codeReview/browser.ts} +53 -33
  50. package/extension/{doors/hunkHandoff.ts → pi/v1/codeReview/checkout.ts} +12 -8
  51. package/extension/{doors/reviewWaveTools.ts → pi/v1/codeReview/reviewWave.ts} +146 -114
  52. package/extension/{doors/stackReviewBrowser.ts → pi/v1/codeReview/stack.ts} +62 -29
  53. package/extension/pi/v1/codeReview/submit.ts +354 -0
  54. package/extension/{doors/prReviewTerminal.ts → pi/v1/codeReview/terminal.ts} +32 -27
  55. package/extension/pi/v1/contextEvidence.ts +80 -0
  56. package/extension/pi/v1/contextInjection.ts +207 -0
  57. package/extension/{doors → pi/v1/delivery}/address.ts +154 -267
  58. package/extension/pi/v1/delivery/ci.ts +570 -0
  59. package/extension/pi/v1/delivery/commitCompact.ts +201 -0
  60. package/extension/pi/v1/delivery/conflictResolverEngine.ts +425 -0
  61. package/extension/{doors → pi/v1/delivery}/land.ts +123 -61
  62. package/extension/pi/v1/delivery/ready.ts +322 -0
  63. package/extension/pi/v1/delivery/stackConflictResolver.ts +172 -0
  64. package/extension/pi/v1/delivery/stackDrive.ts +120 -0
  65. package/extension/pi/v1/delivery/stackLand.ts +223 -0
  66. package/extension/pi/v1/delivery/stackRecover.ts +265 -0
  67. package/extension/pi/v1/delivery/stackStatus.ts +237 -0
  68. package/extension/pi/v1/delivery/stackSync.ts +658 -0
  69. package/extension/pi/v1/delivery/submit.ts +389 -0
  70. package/extension/pi/v1/delivery/submitConflict.ts +186 -0
  71. package/extension/pi/v1/draftReview.ts +431 -0
  72. package/extension/{doors → pi/v1}/draftReviewWaveTools.ts +141 -151
  73. package/extension/pi/v1/gist.ts +794 -0
  74. package/extension/pi/v1/learning/audit.ts +186 -0
  75. package/extension/pi/v1/learning/dream.ts +207 -0
  76. package/extension/{doors/learnFactory.ts → pi/v1/learning/factory.ts} +18 -65
  77. package/extension/{doors/harvestWaveTools.ts → pi/v1/learning/harvest.ts} +46 -100
  78. package/extension/pi/v1/learning/learn.ts +585 -0
  79. package/extension/pi/v1/lifecycleGates.ts +127 -0
  80. package/extension/{factories → pi/v1}/objective.ts +53 -33
  81. package/extension/pi/v1/objectiveAuthoring.ts +672 -0
  82. package/extension/pi/v1/objectiveDreamGate.ts +160 -0
  83. package/extension/{factories/objectivePlan.ts → pi/v1/objectivePlanning.ts} +328 -533
  84. package/extension/pi/v1/objectiveRefinement.ts +1320 -0
  85. package/extension/pi/v1/objectiveReview.ts +451 -0
  86. package/extension/{doors → pi/v1}/objectiveReviewBrowser.ts +259 -172
  87. package/extension/pi/v1/plan.ts +812 -0
  88. package/extension/pi/v1/planReview.ts +820 -0
  89. package/extension/{doors → pi/v1}/planReviewBrowser.ts +228 -152
  90. package/extension/{doors/annotationPush.ts → pi/v1/providers/annotations.ts} +158 -89
  91. package/extension/pi/v1/providers/plannotator.ts +487 -0
  92. package/extension/{doors → pi/v1/providers}/plannotatorHandoff.ts +73 -27
  93. package/extension/pi/v1/providers/selection.ts +43 -0
  94. package/extension/{adapters/planAdapterTombell.ts → pi/v1/providers/tombell.ts} +43 -72
  95. package/extension/pi/v1/review.ts +538 -0
  96. package/extension/pi/v1/reviewOutcome.ts +9 -0
  97. package/extension/pi/v1/scoutWave.ts +318 -0
  98. package/extension/{doors → pi/v1}/selfcheck.ts +4 -4
  99. package/extension/session/branchWorkflowSession.ts +60 -0
  100. package/extension/session/lifecycle.ts +644 -0
  101. package/extension/session/lifecycleGates.ts +64 -0
  102. package/extension/session/saveDestination.ts +87 -0
  103. package/extension/session/workflowSession.ts +971 -0
  104. package/extension/substrate/agentScratch.ts +27 -54
  105. package/extension/substrate/bashScanTimeout.ts +181 -0
  106. package/extension/substrate/bindingDelivery.ts +38 -30
  107. package/extension/substrate/bindings.ts +4 -5
  108. package/extension/substrate/cache.ts +64 -12
  109. package/extension/substrate/childRestrictions.ts +39 -0
  110. package/extension/substrate/coldDoor.ts +17 -1
  111. package/extension/substrate/config.ts +157 -21
  112. package/extension/substrate/git.ts +88 -6
  113. package/extension/substrate/modelVisible.ts +53 -0
  114. package/extension/substrate/prompts.ts +22 -0
  115. package/extension/substrate/registry.ts +2 -0
  116. package/extension/substrate/resolverLease.ts +5 -4
  117. package/extension/substrate/sessionData.ts +85 -152
  118. package/extension/substrate/toolGating.ts +263 -84
  119. package/extension/substrate/unifiedDiff.ts +1 -1
  120. package/extension/substrate/workflowState.ts +178 -163
  121. package/extension/substrate/worktreeResolverLock.ts +261 -0
  122. package/extension/surfaces/surfaces.ts +79 -27
  123. package/extension/waves/adversarialReviewWave.ts +87 -46
  124. package/extension/waves/blockedReports.ts +59 -0
  125. package/extension/waves/draftReviewWave.ts +42 -42
  126. package/extension/waves/laneIdentity.ts +77 -0
  127. package/extension/waves/objectiveExplorerWave.ts +24 -24
  128. package/extension/waves/prReviewWave.ts +89 -77
  129. package/extension/waves/reportWave.ts +438 -578
  130. package/extension/waves/reviewClassifierWave.ts +22 -22
  131. package/extension/waves/rpcAdapter.ts +100 -15
  132. package/extension/waves/scoutWave.ts +192 -0
  133. package/extension/waves/transport.ts +480 -0
  134. package/extension/worker/sdkAdapter.ts +494 -0
  135. package/extension/worker/stageExecution.ts +679 -0
  136. package/extension/workerMain.ts +18 -19
  137. package/package.json +6 -4
  138. package/prompts/_fixtures/live.yaml +43 -18
  139. package/prompts/contexts/adapters/plannotator-gist.md +6 -0
  140. package/prompts/contexts/adapters/plannotator-objective.md +6 -0
  141. package/prompts/contexts/adapters/plannotator-plan.md +8 -1
  142. package/prompts/contexts/adapters/plannotator-refinement.md +22 -0
  143. package/prompts/contexts/objective-refinement.md +17 -0
  144. package/prompts/contexts/read-only.md +1 -1
  145. package/prompts/stages/conflict-resolution-continuation.md +9 -6
  146. package/prompts/stages/conflict-resolution.md +4 -4
  147. package/prompts/stages/objective-plan/guidance.md +2 -2
  148. package/prompts/stages/objective-plan/seed.md +9 -1
  149. package/prompts/stages/objective-reconcile-ready.md +1 -1
  150. package/prompts/stages/objective-reconcile.md +1 -1
  151. package/prompts/stages/objective-refine/seed.md +18 -0
  152. package/prompts/stages/objective-review-browser.md +4 -4
  153. package/prompts/stages/objective-sync.md +1 -1
  154. package/prompts/stages/plan-review-browser.md +4 -4
  155. package/prompts/stages/pr-review-browser/active.md +3 -4
  156. package/prompts/stages/pr-review-browser/foreign.md +3 -4
  157. package/prompts/stages/pr-review-terminal/active.md +3 -3
  158. package/prompts/stages/pr-review-terminal/foreign.md +3 -3
  159. package/prompts/stages/pr-review.md +3 -3
  160. package/prompts/stages/stack-review-browser/stack.md +5 -6
  161. package/shared/README.md +8 -0
  162. package/shared/bindings.yaml +3 -3
  163. package/shared/contracts.md +2601 -506
  164. package/shared/fixtures/issues-table.json +130 -0
  165. package/shared/registry.yaml +13 -0
  166. package/shared/schemas/outputs/objective-node-engagement.schema.json +318 -0
  167. package/shared/schemas/outputs/objective-stack-status.schema.json +6 -1
  168. package/shared/schemas/outputs/pr-review-context.schema.json +54 -9
  169. package/shared/schemas/outputs/pr-review-stack-context.schema.json +196 -0
  170. package/extension/adapters/planAdapterPlannotator.ts +0 -362
  171. package/extension/doors/auditWaveTools.ts +0 -352
  172. package/extension/doors/ciExecutor.ts +0 -756
  173. package/extension/doors/commitCompact.ts +0 -251
  174. package/extension/doors/dreamWaveTools.ts +0 -489
  175. package/extension/doors/learn.ts +0 -668
  176. package/extension/doors/lifecycleGates.ts +0 -207
  177. package/extension/doors/objectiveStack.ts +0 -1543
  178. package/extension/doors/prReviewDynamic.ts +0 -276
  179. package/extension/doors/ready.ts +0 -279
  180. package/extension/doors/submit.ts +0 -373
  181. package/extension/doors/submitPrReview.ts +0 -505
  182. package/extension/factories/gistAuthor.ts +0 -94
  183. package/extension/factories/gistDraft.ts +0 -265
  184. package/extension/factories/gistSave.ts +0 -251
  185. package/extension/factories/implementHere.ts +0 -116
  186. package/extension/factories/objectiveAuthor.ts +0 -98
  187. package/extension/factories/objectiveDraft.ts +0 -466
  188. package/extension/factories/objectiveSave.ts +0 -366
  189. package/extension/factories/planDraft.ts +0 -140
  190. package/extension/factories/planMode.ts +0 -205
  191. package/extension/factories/planReview.ts +0 -1237
  192. package/extension/factories/planSave.ts +0 -604
  193. package/extension/factories/planTitle.ts +0 -141
  194. package/extension/substrate/structuredOutput.ts +0 -202
  195. package/extension/waves/auditWave.ts +0 -312
  196. package/extension/waves/harvestWave.ts +0 -399
  197. package/extension/waves/learnWave.ts +0 -155
  198. package/extension/waves/memoryAdapter.ts +0 -139
  199. package/extension/waves/prReviewDynamicWave.ts +0 -777
  200. package/extension/worker/readOnlySession.ts +0 -294
  201. package/extension/worker/worker.ts +0 -899
  202. package/prompts/stages/pr-review-dynamic.md +0 -7
@@ -0,0 +1,207 @@
1
+ // The one marker-dedup'd context-injection mechanism (contracts §8.31 semantics) behind the
2
+ // six injected authoring/adapter contexts: gist, plan, objective-authoring, refinement and the
3
+ // plannotator/tombell plan adapters all register the same `before_agent_start` + `context` hook
4
+ // pair around one injected, marker-dedup'd context. The MECHANICS live here — the live-evidence
5
+ // dedup, the selection-driven retention filter, the guarded reads, the runner fence. Callers keep
6
+ // flavor selection + content construction (the customType/marker vocabulary) in their
7
+ // `InjectedContextSpec` closures; `isPlanGuidanceStage` is the one shared stage predicate (the
8
+ // plan context and the two plan-adapter plan flavors).
9
+ //
10
+ // ONE decision drives both hooks: `spec.select` (the flavor to deliver this turn, or null). It
11
+ // reads the FULL branch (`branchOf` — eligibility/state survive compaction), while the dedup
12
+ // reads Pi's OWN live projection (`contextEvidence.ts`: `buildContextEntries()` → native
13
+ // messages) and asks the typed predicate whether the selected flavor's marker is still delivered
14
+ // — as user content (a cold prompt) or as the owned customType's content (a prior hidden copy).
15
+ // A copy Pi has compacted out of context re-injects on the next turn even though the historical
16
+ // entry still sits on the branch; a summary quoting the marker never counts. The submitting
17
+ // `event.prompt` is checked BEFORE the projection read: at `before_agent_start` a cold launch's
18
+ // prompt is not yet persisted, so only that check sees a cold seed on the launch turn.
19
+ //
20
+ // RETENTION follows SELECTION on the `context` event, and touches ONLY the owned customType: a
21
+ // null selection (ineligible, or eligibility could not be established — a failed branch read or
22
+ // a throwing selector) removes every owned custom message; a selected flavor retains only the
23
+ // owned copies carrying that flavor's marker and removes obsolete sibling flavors (a
24
+ // plan→objective transition under plannotator cannot retain the old plan-adapter instructions).
25
+ // User/task messages are NEVER removed for carrying an owned marker — a cold seed, a quoted
26
+ // marker, an `<untrusted_draft>` body are the human's/the door's input and stay byte-for-byte;
27
+ // assistant/tool messages and other features' custom messages are never inspected. This filters
28
+ // the OUTGOING model context only: persisted transcripts and compaction summaries are never
29
+ // rewritten.
30
+ //
31
+ // Failure semantics: a failed branch read short-circuits INJECTION (no `select` call — an
32
+ // empty-branch fallback would wrongly inject for a selector whose reads pass on `undefined`); a
33
+ // failed projection read also returns without constructing or injecting content (a guessed copy
34
+ // could double-deliver); on the `context` event the same failure fails CLOSED to "nothing
35
+ // selected" — stale owned guidance must never survive an unreadable branch. Retention never
36
+ // reads the projection.
37
+ //
38
+ // Deliberate NON-callers keep their own scan/strip semantics: `substrate/bindingDelivery.ts`
39
+ // (strips only its own customType — never user turns; projection errors escape its hook),
40
+ // `substrate/agentScratch.ts` (requires the exact current custom block, not a marker scan),
41
+ // `substrate/toolGating.ts` (full-branch scan — the strict once-per-selected-branch read-only
42
+ // marker; its `perk:mode-context` retention is independent of every authoring context here),
43
+ // and `hunkFeedback/receiver.ts`.
44
+
45
+ import type { ExtensionAPI, ExtensionContext } from "@earendil-works/pi-coding-agent";
46
+ import { GIST_AUTHOR_STAGE } from "../../authoring/gist/draft.ts";
47
+ import { OBJECTIVE_AUTHOR_STAGE, OBJECTIVE_SAVE_STAGE } from "../../authoring/objective/prose.ts";
48
+ import { REFINE_STAGE } from "../../authoring/refinement/context.ts";
49
+ import { type BranchEntry, branchOf } from "../../substrate/workflowState.ts";
50
+ import {
51
+ activeContextMessages,
52
+ type ContextMessage,
53
+ contextCarriesMarker,
54
+ } from "./contextEvidence.ts";
55
+
56
+ /**
57
+ * The stages plan guidance is withheld from, for one of two reasons: another authoring context
58
+ * OWNS the stage (the objective/gist/refinement installers inject their own contexts;
59
+ * `objective-save` rides `plan_review`'s objective-arm routing), or the stage is read-only but
60
+ * authors nothing (`audit`, the dev-only `perk-dev audit judge` door — a literal: it has no
61
+ * authoring module). The registry guard test pins the set against the read-only stages.
62
+ */
63
+ const PLAN_GUIDANCE_EXCLUDED_STAGES: ReadonlySet<string> = new Set([
64
+ OBJECTIVE_AUTHOR_STAGE,
65
+ OBJECTIVE_SAVE_STAGE,
66
+ GIST_AUTHOR_STAGE,
67
+ REFINE_STAGE,
68
+ "audit",
69
+ ]);
70
+
71
+ /**
72
+ * Plan guidance rides the read-only gate in every stage not excluded above: the stage-less warm
73
+ * `/plan` (`undefined`), the cold `plan`/`objective-plan` claims, and any other (unknown or
74
+ * read-write) stage id — fail-open like the stage tool filter.
75
+ */
76
+ export function isPlanGuidanceStage(stage: string | undefined): boolean {
77
+ return stage === undefined || !PLAN_GUIDANCE_EXCLUDED_STAGES.has(stage);
78
+ }
79
+
80
+ /**
81
+ * One marker-dedup'd injected context: the owned customType + the flavor table. Each flavor is
82
+ * declared ONCE — the marker literal keys its content thunk — so the retention key, the dedup
83
+ * key (the selected key), and the injected content cannot drift apart: `select` returns a KEY of
84
+ * the table, never a free-floating marker/content pair.
85
+ */
86
+ export interface InjectedContextSpec<K extends string = string> {
87
+ customType: string;
88
+ /**
89
+ * The flavor table: marker literal → content thunk (plannotator: all three flavor markers).
90
+ * A thunk is invoked ONLY after the dedup scan passes, preserving the scan-before-construct
91
+ * ordering (config reads/renders never run on dedup-suppressed turns). Caller contract: the
92
+ * rendered content carries its own key (the marker rides inside the template bytes — pinned by
93
+ * each caller's content tests); a marker-less content would defeat the dedup (re-inject every
94
+ * turn) AND the retention (an owned copy without its flavor's marker is removed as obsolete).
95
+ */
96
+ flavors: Readonly<Record<K, (ctx: ExtensionContext) => string>>;
97
+ /**
98
+ * Feature policy — the ONE eligibility/retention decision: the flavor to deliver this turn (a
99
+ * `flavors` key), or null (ineligible/defer). Drives injection on `before_agent_start` AND the
100
+ * owned-message retention on `context` (null removes every owned copy; a key retains only that
101
+ * flavor's copies).
102
+ */
103
+ select(ctx: ExtensionContext, branch: readonly BranchEntry[]): K | null;
104
+ }
105
+
106
+ /** The structural message slice the retention filter inspects. */
107
+ interface OwnedMessage {
108
+ customType?: string;
109
+ content?: unknown;
110
+ }
111
+
112
+ /**
113
+ * The owned-message retention filter (module-private — tests drive it through the registered
114
+ * hook): messages of the owned customType survive only when a flavor is selected AND their
115
+ * content carries that flavor's marker (a string-content or text-part scan of the OWNED copy
116
+ * only); every other message — any role, any other customType — passes through untouched, marker
117
+ * or not.
118
+ */
119
+ function retainSelectedFlavor<T>(messages: T[], customType: string, marker: string | null): T[] {
120
+ return messages.filter((m) => {
121
+ const msg = m as OwnedMessage;
122
+ if (msg.customType !== customType) return true;
123
+ return marker !== null && carriesMarker(msg.content, marker);
124
+ });
125
+ }
126
+
127
+ function carriesMarker(content: unknown, marker: string): boolean {
128
+ if (typeof content === "string") return content.includes(marker);
129
+ if (Array.isArray(content)) {
130
+ return content.some(
131
+ (c) =>
132
+ (c as { type?: string; text?: string }).type === "text" &&
133
+ ((c as { text?: string }).text ?? "").includes(marker),
134
+ );
135
+ }
136
+ return false;
137
+ }
138
+
139
+ /**
140
+ * Register the inject/retain hook pair for one marker-dedup'd context (contracts §8.31
141
+ * semantics). Call at the exact registration position the replaced hook pair held — hook
142
+ * ordering is frozen by each installer's internal sequence.
143
+ *
144
+ * - `before_agent_start`: guarded branch read (a failed read short-circuits — no `select` call,
145
+ * no injection) → the runner fence (a runner child selects nothing — no `select` call) →
146
+ * `spec.select` (null → no injection) → the submitting prompt carrying the SELECTED marker
147
+ * suppresses (cold delivery before persistence; another flavor's marker does not) → guarded
148
+ * projection read (a failed read returns — nothing constructed, nothing injected) → a live
149
+ * owned copy of the selected marker suppresses (the content thunk is never invoked) → inject
150
+ * `{ customType, content, display: false }`.
151
+ * - `context`: guarded branch read + the same fence + `spec.select` (a failed read or a throwing
152
+ * selector fails closed to null) → retain the owned customType's copies carrying the selected
153
+ * flavor's marker only; remove every other owned copy (all of them on null). No other message
154
+ * is inspected.
155
+ *
156
+ * `runnerChild` is the composition root's per-`session_start` runner bit (§8.3): suppression
157
+ * only, never a grant — and ahead of `spec.select`, so a runner child performs no selector reads.
158
+ */
159
+ export function installInjectedContext<K extends string>(
160
+ pi: ExtensionAPI,
161
+ spec: InjectedContextSpec<K>,
162
+ runnerChild: () => boolean,
163
+ ): void {
164
+ // Defensive over a widened K (string): an off-table key names no flavor — never inject or
165
+ // retain on it.
166
+ const flavorOf = (key: K | null): string | null =>
167
+ key !== null && spec.flavors[key] !== undefined ? key : null;
168
+ const selectedMarker = (ctx: ExtensionContext, branch: readonly BranchEntry[]): string | null =>
169
+ runnerChild() ? null : flavorOf(spec.select(ctx, branch));
170
+
171
+ pi.on("before_agent_start", async (event, ctx) => {
172
+ let branch: readonly BranchEntry[];
173
+ try {
174
+ branch = branchOf(ctx);
175
+ } catch {
176
+ return;
177
+ }
178
+ const marker = selectedMarker(ctx, branch);
179
+ if (marker === null) return;
180
+ const content = spec.flavors[marker as K];
181
+ if (event.prompt.includes(marker)) return;
182
+ let live: readonly ContextMessage[];
183
+ try {
184
+ live = activeContextMessages(ctx);
185
+ } catch {
186
+ return;
187
+ }
188
+ if (contextCarriesMarker(live, { customType: spec.customType, marker })) return;
189
+ return {
190
+ message: {
191
+ customType: spec.customType,
192
+ content: content(ctx),
193
+ display: false,
194
+ },
195
+ };
196
+ });
197
+
198
+ pi.on("context", async (event, ctx) => {
199
+ let selected: string | null;
200
+ try {
201
+ selected = selectedMarker(ctx, branchOf(ctx));
202
+ } catch {
203
+ selected = null;
204
+ }
205
+ return { messages: retainSelectedFlavor(event.messages, spec.customType, selected) };
206
+ });
207
+ }