pi-continual-harness 0.16.1 → 0.17.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.
package/README.md CHANGED
@@ -473,6 +473,17 @@ registerProposer({
473
473
  Then `"my-proposer"` is selectable via `/refine --proposer my-proposer` or
474
474
  `"proposer": "my-proposer"` in the config.
475
475
 
476
+ **Cross-instance registry (0.17+).** pi's extension loader isolates module
477
+ instances between extensions, so before 0.17.0 a companion's `registerProposer`
478
+ call wrote to a private module copy that `/refine` never read — the
479
+ registration was silently invisible. The registry is now backed by a
480
+ process-wide global (`Symbol.for("pi-continual-harness.proposers")`), so any
481
+ copy's registrations are visible to every copy. Companions that want a
482
+ zero-import startup entry can skip importing this package at runtime entirely
483
+ and push a `DeltaProposer` (typically a lazy delegate that dynamic-imports its
484
+ implementation on first `propose()`) directly into
485
+ `globalThis[Symbol.for("pi-continual-harness.proposers")]`.
486
+
476
487
  ## Scope and non-goals
477
488
 
478
489
  - **In scope:** unified state store, online `/refine`, structured deltas,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pi-continual-harness",
3
- "version": "0.16.1",
3
+ "version": "0.17.0",
4
4
  "description": "Online self-improvement layer for pi. A unified, in-trajectory harness-state store (prompt notes, memory, skill descriptions, sub-agent specs) with a manual /refine that proposes evidence-backed structured CRUD deltas. Composes with pi-reflect (offline) and pi-mem (storage); owns only the online optimizer layer. Inspired by Continual Harness (arXiv 2605.09998) and ACE (arXiv 2510.04618).",
5
5
  "keywords": [
6
6
  "pi-package",
package/src/proposer.ts CHANGED
@@ -434,21 +434,55 @@ export const dedupeProposer: DeltaProposer = {
434
434
 
435
435
  // ---- registry -------------------------------------------------------------
436
436
 
437
- const registry = new Map<string, DeltaProposer>([
437
+ // Cross-instance registry key (0.17.0). pi's extension loader isolates module
438
+ // instances between extensions (a fresh jiti per extension load, module cache
439
+ // off), so a companion package's `import { registerProposer } from
440
+ // "pi-continual-harness"` resolves to a DIFFERENT copy of this module than the
441
+ // one whose /refine reads the registry — a module-level Map made companion
442
+ // registrations silently invisible. Backing the registry with a process-wide
443
+ // global makes registration work no matter which copy each side holds.
444
+ //
445
+ // The string is the public contract: companions that avoid importing this
446
+ // package at all (thin startup entries) can push a DeltaProposer straight into
447
+ // `globalThis[Symbol.for("pi-continual-harness.proposers")]`.
448
+ const REGISTRY_KEY = Symbol.for("pi-continual-harness.proposers");
449
+ type ProposerRegistry = Map<string, DeltaProposer>;
450
+
451
+ function globalRegistry(): ProposerRegistry {
452
+ const g = globalThis as Record<symbol, unknown>;
453
+ let r = g[REGISTRY_KEY] as ProposerRegistry | undefined;
454
+ if (!(r instanceof Map)) {
455
+ r = new Map<string, DeltaProposer>();
456
+ g[REGISTRY_KEY] = r;
457
+ }
458
+ return r;
459
+ }
460
+
461
+ // Built-ins stay module-local: they are pure functions of their input, and
462
+ // keeping them off the shared map means two isolated harness copies never hand
463
+ // each other's closures to /refine. External registrations live ONLY in the
464
+ // shared map, so every copy can see them.
465
+ const builtins = new Map<string, DeltaProposer>([
438
466
  ["steering", steeringProposer],
439
467
  ["dedupe", dedupeProposer],
440
468
  ]);
441
469
 
442
- /** Register (or replace) a named proposer. For external extensions. */
470
+ /** Register (or replace) a named proposer. For external extensions. Writes to
471
+ * the process-wide registry (Symbol.for("pi-continual-harness.proposers")),
472
+ * so it takes effect across pi's per-extension module isolation. */
443
473
  export function registerProposer(p: DeltaProposer): void {
444
- registry.set(p.name, p);
474
+ globalRegistry().set(p.name, p);
445
475
  }
446
476
 
447
477
  /** Look up a proposer by name; falls back to the steering default. */
448
478
  export function getProposer(name: string | undefined): DeltaProposer {
449
- return registry.get(name ?? "steering") ?? steeringProposer;
479
+ const key = name ?? "steering";
480
+ const custom = globalRegistry().get(key);
481
+ // Defensive: the shared map can be written by anyone in the process.
482
+ if (custom && typeof custom.propose === "function") return custom;
483
+ return builtins.get(key) ?? steeringProposer;
450
484
  }
451
485
 
452
486
  export function listProposers(): string[] {
453
- return [...registry.keys()];
487
+ return [...new Set([...builtins.keys(), ...globalRegistry().keys()])];
454
488
  }