pi-continual-harness 0.12.0 → 0.13.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 +3 -1
- package/package.json +1 -1
- package/src/auto-refine.ts +46 -1
- package/src/config.ts +7 -1
- package/src/index.ts +11 -0
package/README.md
CHANGED
|
@@ -440,7 +440,9 @@ Then `"my-proposer"` is selectable via `/refine --proposer my-proposer` or
|
|
|
440
440
|
- **Boundary delivery for auto-refine**: the steering prompt is persisted as a
|
|
441
441
|
structural entry with `{ entries, continue: true }` at `turn_end` (no
|
|
442
442
|
synthetic user message, no follow-up-scheduling side effects); manual
|
|
443
|
-
`/refine` still steers via a user message.
|
|
443
|
+
`/refine` still steers via a user message. Each new boundary also omits
|
|
444
|
+
prior auto-refine request drafts from future provider context (pi ≥ 0.87
|
|
445
|
+
context edits; `autoRefine.omitStaleDrafts: false` to keep them).
|
|
444
446
|
|
|
445
447
|
Open extension points (see `docs/ROADMAP.md`):
|
|
446
448
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pi-continual-harness",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.13.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/auto-refine.ts
CHANGED
|
@@ -75,6 +75,39 @@ export function buildRefineDraft(steeringMessage: string): {
|
|
|
75
75
|
return { type: "custom_message", customType: AUTO_REFINE_ENTRY, content: steeringMessage, display: true };
|
|
76
76
|
}
|
|
77
77
|
|
|
78
|
+
/** Structural slice of pi's ProjectedSessionEntry (sourceEntry + messages). */
|
|
79
|
+
export interface ProjectedLike {
|
|
80
|
+
sourceEntry?: { type?: string; customType?: string; id?: string };
|
|
81
|
+
messages?: unknown[];
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/** A context_edit boundary draft (pi ≥ 0.87): omit one entry from future
|
|
85
|
+
* provider context — raw history, usage, and UI history stay untouched. */
|
|
86
|
+
export interface OmissionDraft {
|
|
87
|
+
type: "context_edit";
|
|
88
|
+
targetId: string;
|
|
89
|
+
replacement: null;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Context-edit drafts omitting every LIVE prior auto-refine request from
|
|
94
|
+
* future provider context (pi ≥ 0.87 ContextEditEntry). Invariant: at most
|
|
95
|
+
* ONE auto-refine request is ever model-visible — the newest. Already-omitted
|
|
96
|
+
* drafts surface in the projection with empty `messages` and are skipped, so
|
|
97
|
+
* the operation is idempotent and never stacks redundant edits. Raw history
|
|
98
|
+
* (and the HTML export) keeps every draft: /tree rollback still applies.
|
|
99
|
+
*/
|
|
100
|
+
export function buildOmissionDrafts(projected: Iterable<ProjectedLike>): OmissionDraft[] {
|
|
101
|
+
const drafts: OmissionDraft[] = [];
|
|
102
|
+
for (const p of projected) {
|
|
103
|
+
const src = p.sourceEntry;
|
|
104
|
+
if (src?.type !== "custom_message" || src.customType !== AUTO_REFINE_ENTRY) continue;
|
|
105
|
+
if ((p.messages?.length ?? 0) === 0) continue; // already omitted → skip
|
|
106
|
+
if (typeof src.id === "string") drafts.push({ type: "context_edit", targetId: src.id, replacement: null });
|
|
107
|
+
}
|
|
108
|
+
return drafts;
|
|
109
|
+
}
|
|
110
|
+
|
|
78
111
|
/** Subscribe to turn_end and run /refine on the configured cadence. */
|
|
79
112
|
export function registerAutoRefine(pi: ExtensionAPI): void {
|
|
80
113
|
pi.on("turn_end", async (event, ctx) => {
|
|
@@ -118,7 +151,19 @@ export function registerAutoRefine(pi: ExtensionAPI): void {
|
|
|
118
151
|
pi.sendUserMessage(result.steeringMessage);
|
|
119
152
|
return;
|
|
120
153
|
}
|
|
121
|
-
|
|
154
|
+
// Hygiene (pi ≥ 0.87 context edits): omit every LIVE prior auto-refine
|
|
155
|
+
// request from future provider context, so at most one draft (the new
|
|
156
|
+
// one) is ever model-visible. Already-omitted drafts are skipped by
|
|
157
|
+
// buildOmissionDrafts; raw history and /tree rollback are untouched.
|
|
158
|
+
// Kill-switch: autoRefine.omitStaleDrafts: false.
|
|
159
|
+
const omissions =
|
|
160
|
+
config.autoRefine?.omitStaleDrafts === false
|
|
161
|
+
? []
|
|
162
|
+
: buildOmissionDrafts(event.context?.contextEntries ?? []);
|
|
163
|
+
return {
|
|
164
|
+
entries: [...event.entries, ...omissions, buildRefineDraft(result.steeringMessage)],
|
|
165
|
+
continue: true,
|
|
166
|
+
};
|
|
122
167
|
}
|
|
123
168
|
} catch (err) {
|
|
124
169
|
ctx.ui.notify(`Auto-refine failed: ${(err as Error).message}`, "error");
|
package/src/config.ts
CHANGED
|
@@ -62,6 +62,11 @@ export interface HarnessConfig {
|
|
|
62
62
|
* trajectory contains a durable correction; skip the refine when it says
|
|
63
63
|
* no. Soft-fails to the plain cadence on classifier errors. Default false. */
|
|
64
64
|
gate?: boolean;
|
|
65
|
+
/** Boundary-delivery hygiene (pi ≥ 0.87 context edits, default true): omit
|
|
66
|
+
* prior auto-refine request drafts from future provider context so at most
|
|
67
|
+
* one (the newest) is ever model-visible. Raw history and /tree rollback
|
|
68
|
+
* are untouched; set false to keep every draft in context. */
|
|
69
|
+
omitStaleDrafts?: boolean;
|
|
65
70
|
};
|
|
66
71
|
/** Classifier model for the cheap gated paths (pi ≥ 0.99): "provider/id" or
|
|
67
72
|
* a bare id of a classifier catalog entry (e.g. "typesafe/jev-latest" or a
|
|
@@ -92,7 +97,7 @@ export const DEFAULT_CONFIG: HarnessConfig = {
|
|
|
92
97
|
durableScope: "global",
|
|
93
98
|
autoImport: false,
|
|
94
99
|
remindRefine: { enabled: false, everyTurns: DEFAULT_EVERY_TURNS },
|
|
95
|
-
autoRefine: { enabled: false, everyTurns: DEFAULT_AUTO_EVERY_TURNS, commit: false, gate: false },
|
|
100
|
+
autoRefine: { enabled: false, everyTurns: DEFAULT_AUTO_EVERY_TURNS, commit: false, gate: false, omitStaleDrafts: true },
|
|
96
101
|
classifier: { model: undefined },
|
|
97
102
|
virtualBinding: "virtual",
|
|
98
103
|
proposer: "steering",
|
|
@@ -125,6 +130,7 @@ function mergeConfig(over: Partial<HarnessConfig>): HarnessConfig {
|
|
|
125
130
|
everyTurns: over.autoRefine?.everyTurns ?? DEFAULT_AUTO_EVERY_TURNS,
|
|
126
131
|
commit: over.autoRefine?.commit ?? false,
|
|
127
132
|
gate: over.autoRefine?.gate === true,
|
|
133
|
+
omitStaleDrafts: over.autoRefine?.omitStaleDrafts !== false,
|
|
128
134
|
},
|
|
129
135
|
classifier: {
|
|
130
136
|
model: typeof over.classifier?.model === "string" && over.classifier.model ? over.classifier.model : undefined,
|
package/src/index.ts
CHANGED
|
@@ -139,3 +139,14 @@ export {
|
|
|
139
139
|
type ClassifyBoolResult,
|
|
140
140
|
type ClassifyFn,
|
|
141
141
|
} from "./classify.js";
|
|
142
|
+
|
|
143
|
+
// Auto-refine boundary helpers (pi ≥ 0.87, 0.12.0–0.13.0): the steering draft
|
|
144
|
+
// and the stale-draft context-edit omissions. Re-exported for tests and
|
|
145
|
+
// companion packages without reaching into internal paths.
|
|
146
|
+
export {
|
|
147
|
+
AUTO_REFINE_ENTRY,
|
|
148
|
+
buildOmissionDrafts,
|
|
149
|
+
buildRefineDraft,
|
|
150
|
+
type OmissionDraft,
|
|
151
|
+
type ProjectedLike,
|
|
152
|
+
} from "./auto-refine.js";
|