@aefree/pi-plastic 0.7.1 → 0.7.3

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 CHANGED
@@ -5,7 +5,18 @@ All notable changes to this project will be documented in this file.
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
6
6
  and this project follows semantic versioning for public package releases.
7
7
 
8
- ## Unreleased
8
+ ## [0.7.3] - 2026-09-20
9
+
10
+ ### Changed
11
+
12
+ - Update the Pi development dependencies and dynamic tool-loading eval contract to the 0.86.1 baseline while retaining explicit schema registration and sandbox authorization gates.
13
+
14
+ ## [0.7.2] - 2026-09-19
15
+
16
+ ### Fixed
17
+
18
+ - Stop pending-diff Xlink ownership discovery before querying the workspace root, allowing parent-repository base resolution while preserving linked-repository qualification and refusal on unresolved descendant ownership.
19
+ - Accept both `rep` and `repository` selector spellings when resolving workspace repository identity for pending diffs and shared selector consumers.
9
20
 
10
21
  ## [0.7.1] - 2026-09-17
11
22
 
package/README.md CHANGED
@@ -75,7 +75,7 @@ PI_PLASTIC_TOOL_LOADING_MODE=loader-only # maximum initial schema reduction
75
75
  PI_PLASTIC_TOOL_LOADING_MODE=all-active # all 30 currently exposed tools; loader omitted
76
76
  ```
77
77
 
78
- Pi 0.82 and newer use canonical `sourceInfo` provenance to identify this package's effective tools before deferring, restoring, or activating them. If canonical provenance or ownership of the effective loader cannot be proven, `pi-plastic` fails safe: it preserves the complete current active set exactly, does not defer, remove, or activate any `plastic_*` name, and an effective package loader can only report known tools that are already active rather than activating inactive names. On sourceInfo-capable Pi instances, providers without native deferred definitions still receive the complete current active set after a loader call. Reload or restart Pi after source edits; source files are not watched automatically.
78
+ Pi uses canonical `sourceInfo` provenance to identify this package's effective tools before deferring, restoring, or activating them. If canonical provenance or ownership of the effective loader cannot be proven, `pi-plastic` fails safe: it preserves the complete current active set exactly, does not defer, remove, or activate any `plastic_*` name, and an effective package loader can only report known tools that are already active rather than activating inactive names. On sourceInfo-capable Pi instances, providers without native deferred definitions still receive the complete current active set after a loader call. Reload or restart Pi after source edits; source files are not watched automatically.
79
79
 
80
80
  ## Safety behavior
81
81
 
@@ -119,13 +119,13 @@ pi install npm:@aefree/pi-plastic
119
119
  Install a pinned GitHub release over HTTPS:
120
120
 
121
121
  ```bash
122
- pi install git:github.com/aefreedman/pi-plastic@v0.7.1
122
+ pi install git:github.com/aefreedman/pi-plastic@v0.7.2
123
123
  ```
124
124
 
125
125
  Equivalent SSH install:
126
126
 
127
127
  ```bash
128
- pi install git:git@github.com/aefreedman/pi-plastic@v0.7.1
128
+ pi install git:git@github.com/aefreedman/pi-plastic@v0.7.2
129
129
  ```
130
130
 
131
131
  To intentionally track the moving default branch instead of a release tag:
@@ -151,7 +151,7 @@ Project-local installation protects only Pi processes that load those project se
151
151
  ## Requirements
152
152
 
153
153
  - Node.js 22.19.0 or newer
154
- - Pi 0.82.0 or newer; the current development and eval baseline is Pi 0.83
154
+ - Pi 0.86.1 or newer; the current development and eval baseline is Pi 0.86.1
155
155
  - Plastic SCM / Unity Version Control CLI (`cm`) available on `PATH`, or `PI_PLASTIC_CM_EXECUTABLE` set to its full executable path
156
156
  - A GNU/POSIX-compatible text `diff` available on `PATH`, or `PI_PLASTIC_DIFF_EXECUTABLE` set to its full executable path (including paths containing spaces), for text-only diff tools. Pi invokes that resolved executable as-is and does not discover Git Bash paths automatically. On macOS, bare `diff` is PATH-dependent: it may be GNU Diffutils (often installed as `diff` ahead of system paths) or Apple’s BSD `/usr/bin/diff`; the package does not substitute one for the other. Configure the intended text executable explicitly when that distinction matters.
157
157
  - Patch generation has a separate executable policy: `toolPath` is the one-call highest-priority override, followed by `PI_PLASTIC_PATCH_EXECUTABLE`. On Windows, set one to a verified patch-capable non-GUI executable such as Git's `diff.exe`; the package deliberately does not reuse `PI_PLASTIC_DIFF_EXECUTABLE`/GnuWin32 as a patch default. macOS validation with Plastic 11 found that Apple BSD `/usr/bin/diff` supports text-only diffs but rejects the `--binary` argument supplied by `cm patch`; configure a verified GNU Diffutils-compatible executable explicitly for `plastic_patch`. The non-Windows fallback to `PI_PLASTIC_DIFF_EXECUTABLE` or bare `diff` remains only for environments whose resolved executable satisfies that patch contract. In an interactive Pi session, an invalid configured patch override or missing Windows patch setting produces one package capability warning per Pi runtime; the warning omits configured paths, and `plastic_patch` retains its tool-time validation.
@@ -178,7 +178,7 @@ The live test requires `/main`, no pending changes, and no merge in progress. It
178
178
 
179
179
  ### Dynamic tool-loading eval
180
180
 
181
- The package-local behavioral eval uses fresh Pi 0.83 JSON subprocesses against an explicitly attested dedicated Plastic sandbox and is not a skill eval. It compares all-active, balanced, and loader-only mode behavior, checks exact smallest-sufficient loader activations, blocks destructive calls unless they are supported `preflight: true` previews, captures tool calls and sanitized provider-schema measurements, and deletes raw provider payload captures by default.
181
+ The package-local behavioral eval uses fresh Pi 0.86.1 JSON subprocesses against an explicitly attested dedicated Plastic sandbox and is not a skill eval. It compares all-active, balanced, and loader-only mode behavior, checks exact smallest-sufficient loader activations, blocks destructive calls unless they are supported `preflight: true` previews, captures tool calls and sanitized provider-schema measurements, and deletes raw provider payload captures by default.
182
182
 
183
183
  ```bash
184
184
  npm run eval:tool-loading -- --dry-run
@@ -189,7 +189,7 @@ See [`evals/tool-loading/README.md`](evals/tool-loading/README.md) for approved
189
189
 
190
190
  ## Constrained sampling compatibility
191
191
 
192
- Pi 0.82 introduced provider-side constrained sampling for tools. `pi-plastic` does not currently opt in: every public Plastic schema includes optional fields (at minimum `workdir`), while OpenAI strict function schemas require closed objects and all declared properties to be required. Pi forwards the registered schema without converting those optional fields.
192
+ Pi supports provider-side constrained sampling for tools. `pi-plastic` does not currently opt in: every public Plastic schema includes optional fields (at minimum `workdir`), while OpenAI strict function schemas require closed objects and all declared properties to be required. Pi forwards the registered schema without converting those optional fields.
193
193
 
194
194
  Enabling `strict: "prefer"` now would therefore either produce invalid strict OpenAI requests or require a breaking redesign of the ordinary tool arguments. The test suite audits all registered tools and prevents accidental opt-in until a schema is genuinely strict-compatible. Existing TypeBox validation and Plastic runtime safety checks remain authoritative.
195
195
 
package/index.ts CHANGED
@@ -631,7 +631,7 @@ export default function plasticTools(pi: ExtensionAPI) {
631
631
  const ownership = getEffectivePlasticToolOwnership(pi.getAllTools(), EXTENSION_SOURCE_PATH);
632
632
  const unknownToolNames = getUnknownExactPlasticToolNames(params.toolNames);
633
633
  const active = pi.getActiveTools();
634
- // Never infer ownership without Pi 0.82 canonical sourceInfo. In that
634
+ // Never infer ownership without canonical sourceInfo. In that
635
635
  // compatibility mode we may describe known tools that are already active,
636
636
  // but cannot safely activate an inactive same-named definition.
637
637
  const matches = searchPlasticTools(params, coreDescriptions).filter((match) =>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aefree/pi-plastic",
3
- "version": "0.7.1",
3
+ "version": "0.7.3",
4
4
  "type": "module",
5
5
  "engines": {
6
6
  "node": ">=22.19.0"
@@ -43,8 +43,8 @@
43
43
  },
44
44
  "devDependencies": {
45
45
  "@aefree/pi-file-discovery": "^0.1.1",
46
- "@earendil-works/pi-coding-agent": "0.83.0",
47
- "@earendil-works/pi-tui": "0.83.0",
46
+ "@earendil-works/pi-coding-agent": "0.86.1",
47
+ "@earendil-works/pi-tui": "0.86.1",
48
48
  "@types/node": "22.20.2",
49
49
  "tsx": "4.23.5",
50
50
  "typebox": "^1.3.8",
@@ -1876,7 +1876,7 @@ const parseRepositoryIdentity = (selector: string | undefined): RepositoryIdenti
1876
1876
 
1877
1877
  const parseRepositorySelector = (output: string): RepositoryIdentity | null =>
1878
1878
  {
1879
- const match = output.match(/^\s*repository\s+(?:"([^"]+)"|(\S+))\s*$/mi);
1879
+ const match = output.match(/^\s*(?:repository|rep)\s+(?:"([^"]+)"|(\S+))\s*$/mi);
1880
1880
  return parseRepositoryIdentity(match?.[1] ?? match?.[2]);
1881
1881
  };
1882
1882
 
@@ -1974,6 +1974,12 @@ const createPendingBaseIdentityResolver = (cwd: string, workdir?: string): Pendi
1974
1974
  toPathComparisonKeyFromAbsolutePath(rootIdentity),
1975
1975
  ))
1976
1976
  {
1977
+ // The workspace root belongs to its selector; cm rejects Xlink queries there.
1978
+ if (toPathComparisonKeyFromAbsolutePath(await toFilesystemIdentityPath(candidate))
1979
+ === toPathComparisonKeyFromAbsolutePath(rootIdentity))
1980
+ {
1981
+ break;
1982
+ }
1977
1983
  // A missing ancestor can be a removed Xlink mount. Status does not
1978
1984
  // retain ownership, so parent-repository fallback would be unsafe.
1979
1985
  if ((item.kind === "deleted" || item.kind === "moved") && !(await fs.stat(candidate).catch(() => null)))
@@ -1986,11 +1992,6 @@ const createPendingBaseIdentityResolver = (cwd: string, workdir?: string): Pendi
1986
1992
  item.baseRepository = `${repository.repository}@${repository.server}`;
1987
1993
  return `revid:${item.revisionId}@rep:${repository.repository}@repserver:${repository.server}`;
1988
1994
  }
1989
- if (toPathComparisonKeyFromAbsolutePath(await toFilesystemIdentityPath(candidate))
1990
- === toPathComparisonKeyFromAbsolutePath(rootIdentity))
1991
- {
1992
- break;
1993
- }
1994
1995
  const parent = dirname(candidate);
1995
1996
  if (parent === candidate)
1996
1997
  {
@@ -103,7 +103,7 @@ function hasSourcePath(tool: PlasticToolInfo, expectedSourcePath: string): boole
103
103
  }
104
104
 
105
105
  /**
106
- * Pi 0.82 exposes effective tool provenance. Compare effective definitions to
106
+ * Pi exposes effective tool provenance. Compare effective definitions to
107
107
  * this module's own import path, so even a foreign first-registration-wins
108
108
  * collision on the public loader name cannot become an ownership anchor.
109
109
  * Without that proof, callers preserve the active set unchanged.
@@ -84,7 +84,7 @@ export function parsePlasticSelector(content: string): ParseOutcome<PlasticSelec
84
84
 
85
85
  for (const line of content.split(/\r?\n/)) {
86
86
  if (/^\s*(?:#|$)/.test(line)) continue;
87
- if (/^\s*repository(?:\s|$)/i.test(line)) repository = parseSelectorValue(line, "repository");
87
+ if (/^\s*(?:repository|rep)(?:\s|$)/i.test(line)) repository = parseSelectorValue(line, "(?:repository|rep)");
88
88
  if (/^\s*smartbranch(?:\s|$)/i.test(line)) {
89
89
  sawBranchKey = true;
90
90
  smartBranch = parseSelectorValue(line, "smartbranch");