@quandev104/pi-style 0.1.4 → 0.1.6

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 (34) hide show
  1. package/CHANGELOG.md +30 -0
  2. package/README.md +10 -6
  3. package/dist/extensions/pi-style.js +3939 -1431
  4. package/dist/extensions/pi-style.js.map +1 -1
  5. package/extension-src/pi-style/app/command-service.ts +2 -0
  6. package/extension-src/pi-style/domain/config-authorization.ts +6 -3
  7. package/extension-src/pi-style/domain/config-normalization.ts +21 -5
  8. package/extension-src/pi-style/domain/config-presets.ts +1 -1
  9. package/extension-src/pi-style/domain/config-types.ts +7 -3
  10. package/extension-src/pi-style/domain/theme.ts +6 -1
  11. package/extension-src/pi-style/features/editor/index.ts +169 -27
  12. package/extension-src/pi-style/features/messages/index.ts +66 -0
  13. package/extension-src/pi-style/features/tools/bash-execution.ts +112 -0
  14. package/extension-src/pi-style/features/tools/boxed/bash.ts +154 -130
  15. package/extension-src/pi-style/features/tools/boxed/batch.ts +50 -27
  16. package/extension-src/pi-style/features/tools/boxed/command-shape.ts +136 -0
  17. package/extension-src/pi-style/features/tools/boxed/find.ts +2 -2
  18. package/extension-src/pi-style/features/tools/boxed/gh.ts +1012 -0
  19. package/extension-src/pi-style/features/tools/boxed/git.ts +1960 -0
  20. package/extension-src/pi-style/features/tools/boxed/grep.ts +2 -2
  21. package/extension-src/pi-style/features/tools/boxed/output-tree.ts +9 -10
  22. package/extension-src/pi-style/features/tools/boxed/read.ts +3 -3
  23. package/extension-src/pi-style/features/tools/boxed/write.ts +2 -1
  24. package/extension-src/pi-style/pi/compatibility-coordinator.ts +32 -11
  25. package/extension-src/pi-style/pi/compatibility-probe.ts +341 -205
  26. package/extension-src/pi-style/pi/compatibility-registry.ts +19 -3
  27. package/extension-src/pi-style/pi/index.ts +21 -3
  28. package/extension-src/pi-style/pi/session-coordinator.ts +24 -0
  29. package/extension-src/pi-style/shared/box.ts +8 -4
  30. package/extension-src/pi-style/shared/split-diff.ts +9 -9
  31. package/package.json +9 -9
  32. package/themes/titanium-light.json +82 -0
  33. package/themes/titanium.json +79 -0
  34. package/themes/.gitkeep +0 -0
@@ -3,14 +3,20 @@ import { dirname, join } from "node:path";
3
3
  import { fileURLToPath } from "node:url";
4
4
  import {
5
5
  AssistantMessageComponent,
6
+ BashExecutionComponent,
6
7
  BranchSummaryMessageComponent,
7
8
  CompactionSummaryMessageComponent,
8
9
  CustomMessageComponent,
9
10
  SkillInvocationMessageComponent,
10
11
  ToolExecutionComponent,
11
12
  } from "@earendil-works/pi-coding-agent";
12
- import { decorateMessageRender, type MessageDecorationSnapshot } from "../features/messages/index.js";
13
+ import {
14
+ decorateMessageRender,
15
+ decorateMessageUpdate,
16
+ type MessageDecorationSnapshot,
17
+ } from "../features/messages/index.js";
13
18
  import { renderSpecialMessageBlock, type SpecialBlockSubtype } from "../features/messages/special-blocks.js";
19
+ import { renderBashExecutionBox } from "../features/tools/bash-execution.js";
14
20
  import { createToolDecorationOwner } from "../features/tools/index.js";
15
21
  import {
16
22
  type CompatibilityRecord,
@@ -19,110 +25,130 @@ import {
19
25
  nextGeneration,
20
26
  } from "./compatibility-registry.js";
21
27
 
22
- const PI_VERSION_RANGE = ">=0.83.0 <0.84.0";
23
- const TRUSTED_PI_VERSION = "0.83.0";
24
- const reportStates = new WeakMap<
25
- object,
26
- { records: CompatibilityRecord[]; toolOwner: ReturnType<typeof createToolDecorationOwner> | undefined }
27
- >();
28
- // These fingerprints are coupled to the installed 0.83.0 package source. A matching
29
- // name/arity/hash is evidence of the shipped native method, not a cryptographic proof:
30
- // code loaded before this module could spoof the same function source. We therefore
31
- // fail closed on every unrecorded Pi build and never use module-load capture as trust.
32
- export const TRUSTED_NATIVE_FINGERPRINTS: Readonly<Record<string, string>> = Object.freeze({
33
- "native-assistant-message:render": "2a39243f",
34
- "native-compaction-message:updateDisplay": "f8c44e78",
35
- "native-branch-message:updateDisplay": "415d57b7",
36
- "native-skill-message:updateDisplay": "48099ea6",
37
- "native-custom-message:rebuild": "76ae2e3a",
38
- "tool-call-renderer:getCallRenderer": "951ea0e0",
39
- "tool-result-renderer:getResultRenderer": "8a25cd71",
40
- });
28
+ /**
29
+ * Certification is identity-first, never version-pinned: a surface is certified
30
+ * when the runtime method (or class constructor, for additive installs) matches a
31
+ * recorded name/arity/source-fingerprint identity. Pi version strings are
32
+ * informational only (diagnostics, doctor output) and never gate installation — a
33
+ * version drift that preserves the native identity keeps working, and a drift that
34
+ * changes the identity degrades that single surface to its native fallback while
35
+ * every other surface continues.
36
+ */
37
+ export const SUPPORTED_VERSION_RANGE = ">=0.83.0 <0.85.0";
38
+ export const SUPPORTED_PI_VERSIONS: readonly string[] = Object.freeze(["0.83.0", "0.84.0"]);
41
39
 
42
- export const CERTIFICATION_TABLE = Object.freeze({
43
- "0.83.0": Object.freeze({
44
- "native-assistant-message:render": Object.freeze({
45
- feature: "messages",
46
- subtype: "native-assistant-message",
47
- target: AssistantMessageComponent.prototype,
48
- method: "render",
49
- writable: true,
50
- configurable: true,
40
+ /** A recorded native identity for one certified surface. */
41
+ export interface KnownNativeIdentity {
42
+ readonly name: string;
43
+ readonly arity: number;
44
+ readonly fingerprint: string;
45
+ /** Pi versions known to carry this exact identity (informational only). */
46
+ readonly versions: readonly string[];
47
+ }
48
+
49
+ /**
50
+ * Registry of recorded native identities per surface. A surface matches when the
51
+ * runtime function has the same name, arity, and source fingerprint as one of its
52
+ * identities; the `versions` list only documents where that identity was observed.
53
+ * A matching name/arity/hash is evidence of the shipped native method, not a
54
+ * cryptographic proof: code loaded before this module could spoof the same
55
+ * function source. Unrecorded identities therefore fall back natively per surface.
56
+ */
57
+ export const KNOWN_NATIVE_IDENTITIES: Readonly<Record<string, readonly KnownNativeIdentity[]>> = Object.freeze({
58
+ "native-assistant-message:render": Object.freeze([
59
+ Object.freeze({
51
60
  name: "render",
52
61
  arity: 1,
53
- fingerprint: TRUSTED_NATIVE_FINGERPRINTS["native-assistant-message:render"],
54
- adapterId: "message-prefix-osc133-v1",
55
- status: "certified" as const,
62
+ fingerprint: "2a39243f",
63
+ versions: Object.freeze(["0.83.0", "0.84.0"]),
56
64
  }),
57
- "tool-call-renderer:getCallRenderer": Object.freeze({
58
- feature: "tools",
59
- subtype: "tool-call-renderer",
60
- target: ToolExecutionComponent.prototype,
61
- method: "getCallRenderer",
62
- writable: true,
63
- configurable: true,
64
- name: "getCallRenderer",
65
+ ]),
66
+ "native-assistant-message:updateContent": Object.freeze([
67
+ Object.freeze({
68
+ name: "updateContent",
69
+ arity: 1,
70
+ fingerprint: "4a2f15ff",
71
+ versions: Object.freeze(["0.83.0"]),
72
+ }),
73
+ // 0.84.0 adds an `isStreaming` default parameter and per-part markdown
74
+ // transforms; default parameters do not count toward Function.length, so
75
+ // the arity stays 1 while the source fingerprint changes.
76
+ Object.freeze({
77
+ name: "updateContent",
78
+ arity: 1,
79
+ fingerprint: "d2114491",
80
+ versions: Object.freeze(["0.84.0"]),
81
+ }),
82
+ ]),
83
+ "native-compaction-message:updateDisplay": Object.freeze([
84
+ Object.freeze({
85
+ name: "updateDisplay",
65
86
  arity: 0,
66
- fingerprint: TRUSTED_NATIVE_FINGERPRINTS["tool-call-renderer:getCallRenderer"],
67
- adapterId: "tool-renderer-component-v1",
68
- status: "certified" as const,
87
+ fingerprint: "f8c44e78",
88
+ versions: Object.freeze(["0.83.0", "0.84.0"]),
69
89
  }),
70
- "tool-result-renderer:getResultRenderer": Object.freeze({
71
- feature: "tools",
72
- subtype: "tool-result-renderer",
73
- target: ToolExecutionComponent.prototype,
74
- method: "getResultRenderer",
75
- writable: true,
76
- configurable: true,
77
- name: "getResultRenderer",
90
+ ]),
91
+ "native-branch-message:updateDisplay": Object.freeze([
92
+ Object.freeze({
93
+ name: "updateDisplay",
78
94
  arity: 0,
79
- fingerprint: TRUSTED_NATIVE_FINGERPRINTS["tool-result-renderer:getResultRenderer"],
80
- adapterId: "tool-renderer-component-v1",
81
- status: "certified" as const,
95
+ fingerprint: "415d57b7",
96
+ versions: Object.freeze(["0.83.0", "0.84.0"]),
82
97
  }),
83
- "native-compaction-message:updateDisplay": Object.freeze({
84
- feature: "messages",
85
- subtype: "native-compaction-message",
86
- target: CompactionSummaryMessageComponent.prototype,
87
- method: "updateDisplay",
88
- writable: true,
89
- configurable: true,
90
- adapterId: "message-block-boxed-v1",
91
- status: "certified" as const,
98
+ ]),
99
+ "native-skill-message:updateDisplay": Object.freeze([
100
+ Object.freeze({
101
+ name: "updateDisplay",
102
+ arity: 0,
103
+ fingerprint: "48099ea6",
104
+ versions: Object.freeze(["0.83.0", "0.84.0"]),
92
105
  }),
93
- "native-branch-message:updateDisplay": Object.freeze({
94
- feature: "messages",
95
- subtype: "native-branch-message",
96
- target: BranchSummaryMessageComponent.prototype,
97
- method: "updateDisplay",
98
- writable: true,
99
- configurable: true,
100
- adapterId: "message-block-boxed-v1",
101
- status: "certified" as const,
106
+ ]),
107
+ "native-custom-message:rebuild": Object.freeze([
108
+ Object.freeze({
109
+ name: "rebuild",
110
+ arity: 0,
111
+ fingerprint: "76ae2e3a",
112
+ versions: Object.freeze(["0.83.0", "0.84.0"]),
102
113
  }),
103
- "native-skill-message:updateDisplay": Object.freeze({
104
- feature: "messages",
105
- subtype: "native-skill-message",
106
- target: SkillInvocationMessageComponent.prototype,
107
- method: "updateDisplay",
108
- writable: true,
109
- configurable: true,
110
- adapterId: "message-block-boxed-v1",
111
- status: "certified" as const,
114
+ ]),
115
+ "tool-call-renderer:getCallRenderer": Object.freeze([
116
+ Object.freeze({
117
+ name: "getCallRenderer",
118
+ arity: 0,
119
+ fingerprint: "951ea0e0",
120
+ versions: Object.freeze(["0.83.0", "0.84.0"]),
112
121
  }),
113
- "native-custom-message:rebuild": Object.freeze({
114
- feature: "messages",
115
- subtype: "native-custom-message",
116
- target: CustomMessageComponent.prototype,
117
- method: "rebuild",
118
- writable: true,
119
- configurable: true,
120
- adapterId: "message-block-boxed-v1",
121
- status: "certified" as const,
122
+ ]),
123
+ "tool-result-renderer:getResultRenderer": Object.freeze([
124
+ Object.freeze({
125
+ name: "getResultRenderer",
126
+ arity: 0,
127
+ fingerprint: "8a25cd71",
128
+ versions: Object.freeze(["0.83.0", "0.84.0"]),
129
+ }),
130
+ ]),
131
+ "native-bash-execution:render": Object.freeze([
132
+ Object.freeze({
133
+ // The additive render patch is certified by the class constructor identity
134
+ // (name/arity/source fingerprint): the class defines no own `render`, so
135
+ // the installed own method is the only one and the inherited Container
136
+ // render is the native fallback.
137
+ name: "BashExecutionComponent",
138
+ arity: 2,
139
+ fingerprint: "a5b5abca",
140
+ versions: Object.freeze(["0.83.0", "0.84.0"]),
122
141
  }),
123
- }),
142
+ ]),
124
143
  });
125
144
 
145
+ /** Primary (first-recorded) fingerprint per surface, for diagnostics and back-compat. */
146
+ export const TRUSTED_NATIVE_FINGERPRINTS: Readonly<Record<string, string>> = Object.freeze(
147
+ Object.fromEntries(
148
+ Object.entries(KNOWN_NATIVE_IDENTITIES).map(([key, identities]) => [key, identities[0]!.fingerprint]),
149
+ ),
150
+ );
151
+
126
152
  export interface CompatibilityDescriptorSnapshot {
127
153
  readonly kind: "data" | "accessor";
128
154
  readonly value?: unknown;
@@ -147,7 +173,7 @@ export interface CompatibilityRecordSnapshot {
147
173
 
148
174
  export interface CompatibilityProbeReport {
149
175
  attemptedVersion: string;
150
- matchedCertifiedVersion: string | undefined;
176
+ supportedVersions: readonly string[];
151
177
  certificationTable: typeof CERTIFICATION_TABLE;
152
178
  piVersion: string;
153
179
  versionRange: string;
@@ -158,9 +184,8 @@ export interface CompatibilityProbeReport {
158
184
  certification: readonly {
159
185
  readonly version: string;
160
186
  readonly attemptedVersion: string;
161
- readonly matchedCertifiedVersion: string | undefined;
162
- readonly expected: unknown;
163
- readonly actualPreinstall: CompatibilityDescriptorSnapshot | undefined;
187
+ readonly matchedIdentity: KnownNativeIdentity | undefined;
188
+ readonly knownIdentities: readonly KnownNativeIdentity[];
164
189
  readonly feature: CompatibilityRecord["feature"];
165
190
  readonly subtype: CompatibilityRecord["subtype"];
166
191
  readonly target: object;
@@ -172,6 +197,7 @@ export interface CompatibilityProbeReport {
172
197
  readonly adapterId: string | undefined;
173
198
  readonly status: "certified" | "native-fallback";
174
199
  readonly fallbackReason?: string;
200
+ readonly actualPreinstall: CompatibilityDescriptorSnapshot | undefined;
175
201
  }[];
176
202
  getRuntimeDiagnostics: () => ReadonlyMap<string, number>;
177
203
  getFinalDiagnostics: () => Readonly<import("../features/tools/index.js").ToolDiagnosticArchive> | undefined;
@@ -187,6 +213,12 @@ export interface TargetSpec {
187
213
  adapterId: string | undefined;
188
214
  status: "certified" | "native-fallback";
189
215
  fallbackReason?: string;
216
+ /** "add-method" installs a new own method; the class constructor fingerprint certifies the target. */
217
+ kind?: "method" | "add-method";
218
+ /** Function name to verify (defaults to `method`; additive patches verify the class constructor name). */
219
+ identityName?: string;
220
+ /** Expected arity (defaults to the standard per-method rule; additive patches verify the constructor arity). */
221
+ arity?: number;
190
222
  }
191
223
 
192
224
  export function fingerprint(value: unknown): string | undefined {
@@ -199,81 +231,38 @@ export function fingerprint(value: unknown): string | undefined {
199
231
  return hash.toString(16).padStart(8, "0");
200
232
  }
201
233
 
202
- function trustedNativeIdentity(spec: TargetSpec, piVersion: string | undefined): unknown {
203
- if (piVersion !== TRUSTED_PI_VERSION) return undefined;
204
- const descriptor = Object.getOwnPropertyDescriptor(spec.target, spec.method);
205
- const value = descriptor?.value;
234
+ function knownIdentityMatches(spec: TargetSpec, value: unknown): KnownNativeIdentity | undefined {
235
+ if (typeof value !== "function") return undefined;
236
+ const hash = fingerprint(value);
206
237
  const key = `${spec.subtype}:${spec.method}`;
207
- if (
208
- descriptor?.writable !== true ||
209
- descriptor.configurable !== true ||
210
- typeof value !== "function" ||
211
- value.name !== spec.method ||
212
- value.length !== (spec.method === "render" ? 1 : 0) ||
213
- fingerprint(value) !== TRUSTED_NATIVE_FINGERPRINTS[key]
214
- )
215
- return undefined;
216
- return value;
238
+ return (KNOWN_NATIVE_IDENTITIES[key] ?? []).find(
239
+ (identity) => value.name === identity.name && value.length === identity.arity && hash === identity.fingerprint,
240
+ );
217
241
  }
218
242
 
219
- export const targetSpecs: readonly TargetSpec[] = [
220
- {
221
- feature: "messages",
222
- subtype: "native-assistant-message",
223
- target: AssistantMessageComponent.prototype,
224
- method: "render",
225
- adapterId: "message-prefix-osc133-v1",
226
- status: "certified",
227
- },
228
- {
229
- feature: "messages",
230
- subtype: "native-compaction-message",
231
- target: CompactionSummaryMessageComponent.prototype,
232
- method: "updateDisplay",
233
- adapterId: "message-block-boxed-v1",
234
- status: "certified",
235
- },
236
- {
237
- feature: "messages",
238
- subtype: "native-branch-message",
239
- target: BranchSummaryMessageComponent.prototype,
240
- method: "updateDisplay",
241
- adapterId: "message-block-boxed-v1",
242
- status: "certified",
243
- },
244
- {
245
- feature: "messages",
246
- subtype: "native-skill-message",
247
- target: SkillInvocationMessageComponent.prototype,
248
- method: "updateDisplay",
249
- adapterId: "message-block-boxed-v1",
250
- status: "certified",
251
- },
252
- {
253
- feature: "messages",
254
- subtype: "native-custom-message",
255
- target: CustomMessageComponent.prototype,
256
- method: "rebuild",
257
- adapterId: "message-block-boxed-v1",
258
- status: "certified",
259
- },
260
- {
261
- feature: "tools",
262
- subtype: "tool-call-renderer",
263
- target: ToolExecutionComponent.prototype,
264
- method: "getCallRenderer",
265
- adapterId: "tool-renderer-component-v1",
266
- status: "certified",
267
- },
268
- {
269
- feature: "tools",
270
- subtype: "tool-result-renderer",
271
- target: ToolExecutionComponent.prototype,
272
- method: "getResultRenderer",
273
- adapterId: "tool-renderer-component-v1",
274
- status: "certified",
275
- },
276
- ];
243
+ function matchedNativeIdentity(spec: TargetSpec): KnownNativeIdentity | undefined {
244
+ if (spec.kind === "add-method") {
245
+ // Additive install: the prototype must not already own the method (it is
246
+ // inherited), the class constructor identity must match a recorded build,
247
+ // and the inherited method becomes the native fallback for the delegate.
248
+ if (Object.getOwnPropertyDescriptor(spec.target, spec.method)) return undefined;
249
+ const ctor = Object.getOwnPropertyDescriptor(spec.target, "constructor")?.value;
250
+ return knownIdentityMatches(spec, ctor);
251
+ }
252
+ const descriptor = Object.getOwnPropertyDescriptor(spec.target, spec.method);
253
+ if (descriptor?.writable !== true || descriptor.configurable !== true) return undefined;
254
+ return knownIdentityMatches(spec, descriptor.value);
255
+ }
256
+
257
+ function trustedNativeIdentity(spec: TargetSpec): unknown {
258
+ const identity = matchedNativeIdentity(spec);
259
+ if (!identity) return undefined;
260
+ if (spec.kind === "add-method") {
261
+ const inherited = Object.getOwnPropertyDescriptor(Object.getPrototypeOf(spec.target), spec.method)?.value;
262
+ return typeof inherited === "function" ? inherited : undefined;
263
+ }
264
+ return Object.getOwnPropertyDescriptor(spec.target, spec.method)?.value;
265
+ }
277
266
 
278
267
  export interface PiVersionResolution {
279
268
  resolvePackageEntry?: (packageName: string) => string;
@@ -345,6 +334,7 @@ function descriptorSnapshot(descriptor: PropertyDescriptor | undefined): Compati
345
334
  function certificationRecord(
346
335
  spec: TargetSpec,
347
336
  attemptedVersion: string | undefined,
337
+ matchedIdentity: KnownNativeIdentity | undefined,
348
338
  evidence = Object.getOwnPropertyDescriptor(spec.target, spec.method),
349
339
  ) {
350
340
  const descriptor = evidence;
@@ -352,9 +342,8 @@ function certificationRecord(
352
342
  return {
353
343
  version: attemptedVersion ?? "unknown",
354
344
  attemptedVersion: attemptedVersion ?? "unknown",
355
- matchedCertifiedVersion: attemptedVersion === TRUSTED_PI_VERSION ? TRUSTED_PI_VERSION : undefined,
356
- expected: undefined,
357
- actualPreinstall: descriptorSnapshot(descriptor),
345
+ matchedIdentity,
346
+ knownIdentities: KNOWN_NATIVE_IDENTITIES[`${spec.subtype}:${spec.method}`] ?? [],
358
347
  feature: spec.feature,
359
348
  subtype: spec.subtype,
360
349
  target: spec.target,
@@ -369,19 +358,18 @@ function certificationRecord(
369
358
  };
370
359
  }
371
360
 
372
- function versionInRange(version: string | undefined): boolean {
373
- return version === TRUSTED_PI_VERSION;
374
- }
375
-
376
- function shape(target: object, method: string): boolean {
377
- const descriptor = Object.getOwnPropertyDescriptor(target, method);
361
+ function shape(spec: TargetSpec): boolean {
362
+ const descriptor = Object.getOwnPropertyDescriptor(spec.target, spec.method);
363
+ // Additive installs need an unowned slot (the method is inherited); every
364
+ // other patch requires the native own writable/configurable method.
365
+ if (spec.kind === "add-method") return descriptor === undefined;
378
366
  return typeof descriptor?.value === "function" && descriptor.writable === true && descriptor.configurable === true;
379
367
  }
380
368
 
381
369
  export interface CompatibilityProbeOptions {
382
370
  markers?: Set<string>;
383
371
  config?: Readonly<{
384
- messages: { enabled: boolean; assistantPrefix: boolean; specialBlocks: boolean };
372
+ messages: { enabled: boolean; assistantPrefix: boolean; specialBlocks: boolean; hideThinkingLabel: boolean };
385
373
  tools: { enabled: boolean; style: string; maxCollapsedLines: number; maxExpandedLines: number; dimOutput: boolean };
386
374
  preset: string;
387
375
  }>;
@@ -407,7 +395,7 @@ function createFallbackRecord(
407
395
  method: spec.method,
408
396
  originalIdentity: Reflect.get(spec.target, spec.method),
409
397
  piVersion: piVersion ?? "unknown",
410
- versionRange: PI_VERSION_RANGE,
398
+ versionRange: SUPPORTED_VERSION_RANGE,
411
399
  shape: "unsupported",
412
400
  diagnostic: reason,
413
401
  generation,
@@ -416,10 +404,10 @@ function createFallbackRecord(
416
404
  };
417
405
  }
418
406
 
419
- function probeDiagnostic(spec: TargetSpec, piVersion: string | undefined, identity: unknown): string {
420
- if (!versionInRange(piVersion)) return "Pi version is unknown or outside the recorded 0.83.0 support build";
421
- if (!shape(spec.target, spec.method)) return "target method shape is not an own writable/configurable function";
422
- if (identity === undefined) return "recorded 0.83.0 native fingerprint, name, or arity did not match";
407
+ function probeDiagnostic(spec: TargetSpec, identity: unknown): string {
408
+ if (!shape(spec)) return "target method shape is not an own writable/configurable function";
409
+ if (identity === undefined)
410
+ return "runtime native identity (name, arity, or source fingerprint) matched no recorded Pi surface";
423
411
  return "exact native identity verified; certified guarded decoration enabled";
424
412
  }
425
413
 
@@ -427,7 +415,9 @@ function surfaceDisabled(spec: TargetSpec, config: CompatibilityProbeOptions["co
427
415
  if (!config) return false;
428
416
  if (spec.feature === "tools") return !config.tools.enabled;
429
417
  if (!config.messages.enabled) return true;
430
- if (spec.subtype === "native-assistant-message") return !config.messages.assistantPrefix;
418
+ if (spec.subtype === "native-assistant-message" && spec.method === "render") return !config.messages.assistantPrefix;
419
+ if (spec.subtype === "native-assistant-message" && spec.method === "updateContent")
420
+ return !config.messages.hideThinkingLabel;
431
421
  if (isSpecialBlock(spec)) return !config.messages.specialBlocks;
432
422
  return true;
433
423
  }
@@ -442,8 +432,8 @@ function probeSpec(options: {
442
432
  toolOwner: ReturnType<typeof createToolDecorationOwner> | undefined;
443
433
  }) {
444
434
  const { spec, piVersion, generation, markers, config, toolOwner, messageSnapshot } = options;
445
- const identity = trustedNativeIdentity(spec, piVersion);
446
- const diagnostic = probeDiagnostic(spec, piVersion, identity);
435
+ const identity = trustedNativeIdentity(spec);
436
+ const diagnostic = probeDiagnostic(spec, identity);
447
437
  const disabled = surfaceDisabled(spec, config);
448
438
  if (disabled) {
449
439
  const reason = "native fallback: surface disabled by normalized configuration";
@@ -455,14 +445,20 @@ function probeSpec(options: {
455
445
  target: spec.target,
456
446
  method: spec.method,
457
447
  piVersion: piVersion ?? "unknown",
458
- versionRange: PI_VERSION_RANGE,
459
- shape: identity !== undefined && versionInRange(piVersion) && shape(spec.target, spec.method),
448
+ versionRange: SUPPORTED_VERSION_RANGE,
449
+ shape: identity !== undefined && shape(spec),
460
450
  generation,
461
451
  expectedIdentity: identity,
462
452
  hasExpectedIdentity: true,
463
453
  diagnostic,
454
+ ...(spec.kind ? { kind: spec.kind } : {}),
464
455
  delegate: (original, target, args) => {
465
456
  markers.add(`${spec.subtype}:delegated`);
457
+ if (spec.subtype === "native-bash-execution")
458
+ return (
459
+ renderBashExecutionBox(target, args) ??
460
+ Reflect.apply(original as (...values: unknown[]) => unknown, target, args)
461
+ );
466
462
  if (spec.feature === "tools")
467
463
  return (
468
464
  toolOwner?.decorateToolRendererSelection(
@@ -472,8 +468,10 @@ function probeSpec(options: {
472
468
  args,
473
469
  ) ?? Reflect.apply(original as (...values: unknown[]) => unknown, target, args)
474
470
  );
475
- if (spec.subtype === "native-assistant-message")
471
+ if (spec.subtype === "native-assistant-message") {
472
+ if (spec.method === "updateContent") return decorateMessageUpdate(original, target, args, messageSnapshot);
476
473
  return decorateMessageRender(original, target, args, messageSnapshot);
474
+ }
477
475
  return renderSpecialMessageBlock(spec.subtype as SpecialBlockSubtype, original, target, args);
478
476
  },
479
477
  });
@@ -484,6 +482,153 @@ function probeSpec(options: {
484
482
  };
485
483
  }
486
484
 
485
+ export const targetSpecs: readonly TargetSpec[] = [
486
+ {
487
+ feature: "messages",
488
+ subtype: "native-assistant-message",
489
+ target: AssistantMessageComponent.prototype,
490
+ method: "render",
491
+ adapterId: "message-prefix-osc133-v1",
492
+ status: "certified",
493
+ },
494
+ {
495
+ feature: "messages",
496
+ subtype: "native-assistant-message",
497
+ target: AssistantMessageComponent.prototype,
498
+ method: "updateContent",
499
+ adapterId: "message-thinking-collapse-v1",
500
+ status: "certified",
501
+ },
502
+ {
503
+ feature: "messages",
504
+ subtype: "native-compaction-message",
505
+ target: CompactionSummaryMessageComponent.prototype,
506
+ method: "updateDisplay",
507
+ adapterId: "message-block-boxed-v1",
508
+ status: "certified",
509
+ },
510
+ {
511
+ feature: "messages",
512
+ subtype: "native-branch-message",
513
+ target: BranchSummaryMessageComponent.prototype,
514
+ method: "updateDisplay",
515
+ adapterId: "message-block-boxed-v1",
516
+ status: "certified",
517
+ },
518
+ {
519
+ feature: "messages",
520
+ subtype: "native-skill-message",
521
+ target: SkillInvocationMessageComponent.prototype,
522
+ method: "updateDisplay",
523
+ adapterId: "message-block-boxed-v1",
524
+ status: "certified",
525
+ },
526
+ {
527
+ feature: "messages",
528
+ subtype: "native-custom-message",
529
+ target: CustomMessageComponent.prototype,
530
+ method: "rebuild",
531
+ adapterId: "message-block-boxed-v1",
532
+ status: "certified",
533
+ },
534
+ {
535
+ feature: "tools",
536
+ subtype: "tool-call-renderer",
537
+ target: ToolExecutionComponent.prototype,
538
+ method: "getCallRenderer",
539
+ adapterId: "tool-renderer-component-v1",
540
+ status: "certified",
541
+ },
542
+ {
543
+ feature: "tools",
544
+ subtype: "tool-result-renderer",
545
+ target: ToolExecutionComponent.prototype,
546
+ method: "getResultRenderer",
547
+ adapterId: "tool-renderer-component-v1",
548
+ status: "certified",
549
+ },
550
+ {
551
+ feature: "tools",
552
+ subtype: "native-bash-execution",
553
+ target: BashExecutionComponent.prototype,
554
+ method: "render",
555
+ kind: "add-method",
556
+ identityName: "BashExecutionComponent",
557
+ arity: 2,
558
+ adapterId: "bash-execution-box-v1",
559
+ status: "certified",
560
+ },
561
+ ];
562
+
563
+ /**
564
+ * Version → surface → recorded certified identity (informational). Certification
565
+ * itself is decided per-surface by the runtime identity; this table only documents
566
+ * which identity each supported Pi version is known to carry.
567
+ */
568
+ export const CERTIFICATION_TABLE: Readonly<
569
+ Record<
570
+ string,
571
+ Readonly<
572
+ Record<
573
+ string,
574
+ Readonly<{
575
+ feature: CompatibilityRecord["feature"];
576
+ subtype: CompatibilityRecord["subtype"];
577
+ target: object;
578
+ method: string;
579
+ writable: boolean;
580
+ configurable: boolean;
581
+ name: string;
582
+ arity: number;
583
+ fingerprint: string;
584
+ adapterId: string | undefined;
585
+ status: "certified";
586
+ }>
587
+ >
588
+ >
589
+ >
590
+ > = Object.freeze(
591
+ Object.fromEntries(
592
+ SUPPORTED_PI_VERSIONS.map((version) => [
593
+ version,
594
+ Object.freeze(
595
+ Object.fromEntries(
596
+ targetSpecs.flatMap((spec) => {
597
+ const key = `${spec.subtype}:${spec.method}`;
598
+ const identity = (KNOWN_NATIVE_IDENTITIES[key] ?? []).find((candidate) =>
599
+ candidate.versions.includes(version),
600
+ );
601
+ if (!identity) return [];
602
+ return [
603
+ [
604
+ key,
605
+ Object.freeze({
606
+ feature: spec.feature,
607
+ subtype: spec.subtype,
608
+ target: spec.target,
609
+ method: spec.method,
610
+ writable: true,
611
+ configurable: true,
612
+ name: identity.name,
613
+ arity: identity.arity,
614
+ fingerprint: identity.fingerprint,
615
+ adapterId: spec.adapterId,
616
+ status: "certified" as const,
617
+ }),
618
+ ],
619
+ ];
620
+ }),
621
+ ),
622
+ ),
623
+ ]),
624
+ ),
625
+ );
626
+
627
+ const reportStates = new WeakMap<
628
+ object,
629
+ { records: CompatibilityRecord[]; toolOwner: ReturnType<typeof createToolDecorationOwner> | undefined }
630
+ >();
631
+
487
632
  export function probePiCompatibility(
488
633
  piVersion: string | undefined,
489
634
  options: Set<string> | CompatibilityProbeOptions = new Set(),
@@ -493,7 +638,7 @@ export function probePiCompatibility(
493
638
  const toolSpecs = targetSpecs.filter((spec) => spec.feature === "tools");
494
639
  let toolOwner: ReturnType<typeof createToolDecorationOwner> | undefined;
495
640
  if (
496
- toolSpecs.some((spec) => trustedNativeIdentity(spec, piVersion) !== undefined) &&
641
+ toolSpecs.some((spec) => trustedNativeIdentity(spec) !== undefined) &&
497
642
  (options instanceof Set || options.config?.tools.enabled !== false)
498
643
  ) {
499
644
  toolOwner = createToolDecorationOwner(options instanceof Set ? {} : options.toolSnapshot);
@@ -516,6 +661,9 @@ export function probePiCompatibility(
516
661
  for (const spec of targetSpecs) {
517
662
  const captured = evidenceByKey.get(`${spec.subtype}:${String(spec.method)}`);
518
663
  const preinstallDescriptor = captured?.descriptor;
664
+ // Captured before install: the runtime identity is still the pristine native
665
+ // method (or class constructor for additive installs) at this point.
666
+ const matchedIdentity = matchedNativeIdentity(spec);
519
667
  const result = probeSpec({
520
668
  messageSnapshot: options instanceof Set ? undefined : options.messageSnapshot,
521
669
  spec,
@@ -526,15 +674,10 @@ export function probePiCompatibility(
526
674
  toolOwner,
527
675
  });
528
676
  records.push(result.record);
529
- const certificate = certificationRecord(spec, piVersion, preinstallDescriptor);
677
+ const certificate = certificationRecord(spec, piVersion, matchedIdentity, preinstallDescriptor);
530
678
  certification.push({
531
679
  ...certificate,
532
680
  attemptedVersion: piVersion ?? "unknown",
533
- matchedCertifiedVersion: piVersion === TRUSTED_PI_VERSION ? TRUSTED_PI_VERSION : undefined,
534
- expected:
535
- CERTIFICATION_TABLE[TRUSTED_PI_VERSION][
536
- `${spec.subtype}:${spec.method}` as keyof (typeof CERTIFICATION_TABLE)["0.83.0"]
537
- ],
538
681
  actualPreinstall: descriptorSnapshot(preinstallDescriptor),
539
682
  status: result.fallback ? "native-fallback" : "certified",
540
683
  ...(result.fallback ? { fallbackReason: result.reason } : {}),
@@ -543,10 +686,10 @@ export function probePiCompatibility(
543
686
  }
544
687
  const report: CompatibilityProbeReport = {
545
688
  attemptedVersion: piVersion ?? "unknown",
546
- matchedCertifiedVersion: piVersion === TRUSTED_PI_VERSION ? TRUSTED_PI_VERSION : undefined,
689
+ supportedVersions: SUPPORTED_PI_VERSIONS,
547
690
  certificationTable: CERTIFICATION_TABLE,
548
691
  piVersion: piVersion ?? "unknown",
549
- versionRange: PI_VERSION_RANGE,
692
+ versionRange: SUPPORTED_VERSION_RANGE,
550
693
  generation: currentGeneration(),
551
694
  recordSnapshots: Object.freeze(
552
695
  records.map((record) =>
@@ -565,14 +708,7 @@ export function probePiCompatibility(
565
708
  ),
566
709
  unsupported: Object.freeze(unsupported.map((item) => Object.freeze({ ...item }))),
567
710
  delegationMarkers: Object.freeze([...markers]),
568
- certification: Object.freeze(
569
- certification.map((item) =>
570
- Object.freeze({
571
- ...item,
572
- actualPreinstall: item.actualPreinstall,
573
- }),
574
- ),
575
- ),
711
+ certification: Object.freeze(certification.map((item) => Object.freeze({ ...item }))),
576
712
  getRuntimeDiagnostics: () => toolOwner?.getDiagnostics() ?? new Map(),
577
713
  getFinalDiagnostics: () => toolOwner?.getFinalArchive(),
578
714
  getActiveToolRecordCount: () => toolOwner?.getActiveRecordCount() ?? 0,