@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.
- package/CHANGELOG.md +30 -0
- package/README.md +10 -6
- package/dist/extensions/pi-style.js +3939 -1431
- package/dist/extensions/pi-style.js.map +1 -1
- package/extension-src/pi-style/app/command-service.ts +2 -0
- package/extension-src/pi-style/domain/config-authorization.ts +6 -3
- package/extension-src/pi-style/domain/config-normalization.ts +21 -5
- package/extension-src/pi-style/domain/config-presets.ts +1 -1
- package/extension-src/pi-style/domain/config-types.ts +7 -3
- package/extension-src/pi-style/domain/theme.ts +6 -1
- package/extension-src/pi-style/features/editor/index.ts +169 -27
- package/extension-src/pi-style/features/messages/index.ts +66 -0
- package/extension-src/pi-style/features/tools/bash-execution.ts +112 -0
- package/extension-src/pi-style/features/tools/boxed/bash.ts +154 -130
- package/extension-src/pi-style/features/tools/boxed/batch.ts +50 -27
- package/extension-src/pi-style/features/tools/boxed/command-shape.ts +136 -0
- package/extension-src/pi-style/features/tools/boxed/find.ts +2 -2
- package/extension-src/pi-style/features/tools/boxed/gh.ts +1012 -0
- package/extension-src/pi-style/features/tools/boxed/git.ts +1960 -0
- package/extension-src/pi-style/features/tools/boxed/grep.ts +2 -2
- package/extension-src/pi-style/features/tools/boxed/output-tree.ts +9 -10
- package/extension-src/pi-style/features/tools/boxed/read.ts +3 -3
- package/extension-src/pi-style/features/tools/boxed/write.ts +2 -1
- package/extension-src/pi-style/pi/compatibility-coordinator.ts +32 -11
- package/extension-src/pi-style/pi/compatibility-probe.ts +341 -205
- package/extension-src/pi-style/pi/compatibility-registry.ts +19 -3
- package/extension-src/pi-style/pi/index.ts +21 -3
- package/extension-src/pi-style/pi/session-coordinator.ts +24 -0
- package/extension-src/pi-style/shared/box.ts +8 -4
- package/extension-src/pi-style/shared/split-diff.ts +9 -9
- package/package.json +9 -9
- package/themes/titanium-light.json +82 -0
- package/themes/titanium.json +79 -0
- 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 {
|
|
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
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
export const
|
|
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
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
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:
|
|
54
|
-
|
|
55
|
-
status: "certified" as const,
|
|
62
|
+
fingerprint: "2a39243f",
|
|
63
|
+
versions: Object.freeze(["0.83.0", "0.84.0"]),
|
|
56
64
|
}),
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
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:
|
|
67
|
-
|
|
68
|
-
status: "certified" as const,
|
|
87
|
+
fingerprint: "f8c44e78",
|
|
88
|
+
versions: Object.freeze(["0.83.0", "0.84.0"]),
|
|
69
89
|
}),
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
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:
|
|
80
|
-
|
|
81
|
-
status: "certified" as const,
|
|
95
|
+
fingerprint: "415d57b7",
|
|
96
|
+
versions: Object.freeze(["0.83.0", "0.84.0"]),
|
|
82
97
|
}),
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
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
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
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
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
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
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
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
|
-
|
|
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
|
|
162
|
-
readonly
|
|
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
|
|
203
|
-
if (
|
|
204
|
-
const
|
|
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
|
-
|
|
208
|
-
|
|
209
|
-
|
|
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
|
-
|
|
220
|
-
{
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
method
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
}
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
{
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
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
|
-
|
|
356
|
-
|
|
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
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
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:
|
|
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,
|
|
420
|
-
if (!
|
|
421
|
-
if (
|
|
422
|
-
|
|
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
|
|
446
|
-
const diagnostic = probeDiagnostic(spec,
|
|
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:
|
|
459
|
-
shape: identity !== undefined &&
|
|
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
|
|
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
|
-
|
|
689
|
+
supportedVersions: SUPPORTED_PI_VERSIONS,
|
|
547
690
|
certificationTable: CERTIFICATION_TABLE,
|
|
548
691
|
piVersion: piVersion ?? "unknown",
|
|
549
|
-
versionRange:
|
|
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,
|