@principles/host-runtime 0.7.10 → 0.7.11
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.
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { type ActivatedPrinciple } from '@principles/core/runtime-v2';
|
|
1
|
+
import { type ActivatedPrinciple, type PromptSelectionPolicy } from '@principles/core/runtime-v2';
|
|
2
2
|
export interface ActivePrinciplePromptResult {
|
|
3
3
|
additionalContext: string;
|
|
4
4
|
principleIds: string[];
|
|
@@ -11,6 +11,16 @@ export interface ActivePrinciplePromptResult {
|
|
|
11
11
|
excludedCount: number;
|
|
12
12
|
exclusionReason?: 'host_principle_overlap';
|
|
13
13
|
allValidatedPrinciplesExcluded: boolean;
|
|
14
|
+
/** PRI-904: budget-packing policy that produced this selection. */
|
|
15
|
+
selectionPolicy?: PromptSelectionPolicy;
|
|
16
|
+
/** PRI-904: candidates that reached the budget selector. */
|
|
17
|
+
eligibleCount?: number;
|
|
18
|
+
/** PRI-904: circular scan start; present only under fair_rotation_v1. */
|
|
19
|
+
rotationStartIndex?: number;
|
|
20
|
+
/** PRI-904 (bounded, max 16): activation ids dropped for insufficient remaining budget. */
|
|
21
|
+
droppedActivationIds?: string[];
|
|
22
|
+
/** PRI-904 (bounded, max 16): activation ids that cannot fit even in an empty payload. */
|
|
23
|
+
oversizedActivationIds?: string[];
|
|
14
24
|
}
|
|
15
25
|
export interface PromptActivationCandidates {
|
|
16
26
|
principles: ActivatedPrinciple[];
|
|
@@ -34,4 +44,10 @@ export declare function readPromptActivationCandidates(input: {
|
|
|
34
44
|
export declare function buildActivePrinciplePromptContext(input: {
|
|
35
45
|
workspaceDir: string;
|
|
36
46
|
excludePrincipleIds?: ReadonlySet<string>;
|
|
47
|
+
/**
|
|
48
|
+
* PRI-904 fair-rotation round key (derive via roundKeyFromRunIdentity from
|
|
49
|
+
* the host run/turn id). Absent → legacy FIFO prefix policy (rollback /
|
|
50
|
+
* characterization baseline).
|
|
51
|
+
*/
|
|
52
|
+
roundKey?: number;
|
|
37
53
|
}): Promise<ActivePrinciplePromptResult>;
|
|
@@ -3,6 +3,8 @@ import fs from 'node:fs';
|
|
|
3
3
|
import path from 'node:path';
|
|
4
4
|
import { escapeXml } from '@principles/core/prompt-builder';
|
|
5
5
|
import { loadPdConfigForPlugin } from './pd-config.js';
|
|
6
|
+
/** PRI-904 SPEC §10: bounded diagnostic id lists (mirrors the core selector's cap of 16). */
|
|
7
|
+
const MAX_SHARED_DIAGNOSTIC_IDS = 16;
|
|
6
8
|
/**
|
|
7
9
|
* PR #1844 follow-up: the activation READ half of buildActivePrinciplePromptContext,
|
|
8
10
|
* exported so the console budget projection can share the exact same input
|
|
@@ -85,17 +87,49 @@ export async function buildActivePrinciplePromptContext(input) {
|
|
|
85
87
|
additionalContext: '', principleIds: [], activationIds: [], artifactIds: [], warnings,
|
|
86
88
|
budget: RUNTIME_V2_PRINCIPLE_BUDGET, truncated: false, excludedPrincipleIds,
|
|
87
89
|
excludedCount: excludedPrincipleIds.length, allValidatedPrinciplesExcluded: false,
|
|
90
|
+
selectionPolicy: 'legacy_fifo_prefix_v1', eligibleCount: 0,
|
|
88
91
|
};
|
|
89
92
|
}
|
|
90
93
|
const included = [];
|
|
91
94
|
let additionalContext = '';
|
|
92
95
|
let truncated = false;
|
|
93
|
-
|
|
96
|
+
// PRI-904: same fair-rotation semantics as the plugin route's trimToBudget —
|
|
97
|
+
// deterministic rotating start over the base (ASC) candidate order, circular
|
|
98
|
+
// scan, continue-on-non-fit. Costs are measured with the REAL serializer
|
|
99
|
+
// (renderPrinciplesToDirectives) so budget accounting stays exact here too.
|
|
100
|
+
const { roundKey } = input;
|
|
101
|
+
const selectionPolicy = roundKey === undefined || principles.length === 0 ? 'legacy_fifo_prefix_v1' : 'fair_rotation_v1';
|
|
102
|
+
const n = principles.length;
|
|
103
|
+
// Guard on the policy, not just the key: an all-excluded candidate list has
|
|
104
|
+
// n === 0 and must not emit a NaN start (core trimToBudget omits the field
|
|
105
|
+
// for empty input — parity requires the same here).
|
|
106
|
+
const rotationStartIndex = selectionPolicy === 'fair_rotation_v1' && roundKey !== undefined
|
|
107
|
+
? ((roundKey % n) + n) % n
|
|
108
|
+
: undefined;
|
|
109
|
+
const droppedActivationIds = [];
|
|
110
|
+
const oversizedActivationIds = [];
|
|
111
|
+
const standaloneFits = (principle) => renderPrinciplesToDirectives([principle], new Set([principle.principleId]), { escapeFn: escapeXml, selfReportInstruction: selfReportEnabled }).length
|
|
112
|
+
<= RUNTIME_V2_PRINCIPLE_BUDGET;
|
|
113
|
+
const scanOrder = rotationStartIndex === undefined
|
|
114
|
+
? principles
|
|
115
|
+
: principles.slice(rotationStartIndex).concat(principles.slice(0, rotationStartIndex));
|
|
116
|
+
for (const principle of scanOrder) {
|
|
94
117
|
const candidate = [...included, principle];
|
|
95
118
|
const candidateContext = renderPrinciplesToDirectives(candidate, new Set(candidate.map((entry) => entry.principleId)), { escapeFn: escapeXml, selfReportInstruction: selfReportEnabled });
|
|
96
119
|
if (candidateContext.length > RUNTIME_V2_PRINCIPLE_BUDGET) {
|
|
97
|
-
|
|
98
|
-
|
|
120
|
+
if (standaloneFits(principle)) {
|
|
121
|
+
// Fits alone but not now — normal budget truncation.
|
|
122
|
+
truncated = true;
|
|
123
|
+
if (droppedActivationIds.length < MAX_SHARED_DIAGNOSTIC_IDS)
|
|
124
|
+
droppedActivationIds.push(principle.activationId);
|
|
125
|
+
}
|
|
126
|
+
else if (oversizedActivationIds.length < MAX_SHARED_DIAGNOSTIC_IDS) {
|
|
127
|
+
// Can never fit alone — oversize, not starvation (PRI-904 SPEC §6.6).
|
|
128
|
+
oversizedActivationIds.push(principle.activationId);
|
|
129
|
+
}
|
|
130
|
+
if (selectionPolicy === 'legacy_fifo_prefix_v1')
|
|
131
|
+
break;
|
|
132
|
+
continue;
|
|
99
133
|
}
|
|
100
134
|
included.push(principle);
|
|
101
135
|
additionalContext = candidateContext;
|
|
@@ -116,5 +150,10 @@ export async function buildActivePrinciplePromptContext(input) {
|
|
|
116
150
|
excludedCount: excludedPrincipleIds.length,
|
|
117
151
|
allValidatedPrinciplesExcluded: excludedPrincipleIds.length > 0 && principles.length === 0,
|
|
118
152
|
...(excludedPrincipleIds.length > 0 ? { exclusionReason: 'host_principle_overlap' } : {}),
|
|
153
|
+
selectionPolicy,
|
|
154
|
+
eligibleCount: principles.length,
|
|
155
|
+
...(rotationStartIndex !== undefined ? { rotationStartIndex } : {}),
|
|
156
|
+
...(droppedActivationIds.length > 0 ? { droppedActivationIds } : {}),
|
|
157
|
+
...(oversizedActivationIds.length > 0 ? { oversizedActivationIds } : {}),
|
|
119
158
|
};
|
|
120
159
|
}
|
package/dist/index.js
CHANGED
|
@@ -177,6 +177,16 @@ export function createProductionHostRuntime(options = {}) {
|
|
|
177
177
|
}),
|
|
178
178
|
beforeToolCall: options.beforeToolCall ?? productionGate,
|
|
179
179
|
async beforePromptBuild(event) {
|
|
180
|
+
// PRI-904 Phase-1 (Option 3): this production path is ALSO the Codex
|
|
181
|
+
// route, and Codex never writes trajectory.db::user_turns — its
|
|
182
|
+
// ingestion writes governance_* rows instead. Reading a session turn
|
|
183
|
+
// ordinal here would therefore be a false round authority: on Codex it
|
|
184
|
+
// either throws (→ legacy) or returns a constant 1 (→ fixed start, a
|
|
185
|
+
// different positional starvation while the event still claimed a fair
|
|
186
|
+
// rotation). So the shared production path passes NO round key and
|
|
187
|
+
// reports `legacy_fifo_prefix_v1` honestly. The selector's fair-rotation
|
|
188
|
+
// capability is retained and stays reachable for callers that DO hold a
|
|
189
|
+
// legitimate advancing round authority.
|
|
180
190
|
const prompt = await buildActivePrinciplePromptContext({
|
|
181
191
|
workspaceDir: event.context.workspaceDir,
|
|
182
192
|
excludePrincipleIds: options.promptExcludePrincipleIds?.(event),
|
|
@@ -203,6 +213,14 @@ export function createProductionHostRuntime(options = {}) {
|
|
|
203
213
|
budget: RUNTIME_V2_PRINCIPLE_BUDGET,
|
|
204
214
|
...(prompt.truncated !== undefined ? { v2Truncated: prompt.truncated } : {}),
|
|
205
215
|
...(event.context.turnId !== undefined ? { runId: event.context.turnId } : {}),
|
|
216
|
+
// PRI-904: selection diagnostics (bounded, optional). On this route
|
|
217
|
+
// the policy is legacy_fifo_prefix_v1 and NO rotation provenance is
|
|
218
|
+
// emitted — a fair claim would be untrue here (see above).
|
|
219
|
+
...(prompt.selectionPolicy !== undefined ? { selectionPolicy: prompt.selectionPolicy } : {}),
|
|
220
|
+
...(prompt.eligibleCount !== undefined ? { eligibleCount: prompt.eligibleCount } : {}),
|
|
221
|
+
...(prompt.rotationStartIndex !== undefined ? { rotationStartIndex: prompt.rotationStartIndex } : {}),
|
|
222
|
+
...(prompt.droppedActivationIds !== undefined && prompt.droppedActivationIds.length > 0 ? { droppedActivationIds: prompt.droppedActivationIds } : {}),
|
|
223
|
+
...(prompt.oversizedActivationIds !== undefined && prompt.oversizedActivationIds.length > 0 ? { oversizedActivationIds: prompt.oversizedActivationIds } : {}),
|
|
206
224
|
});
|
|
207
225
|
}
|
|
208
226
|
catch (err) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@principles/host-runtime",
|
|
3
|
-
"version": "0.7.
|
|
3
|
+
"version": "0.7.11",
|
|
4
4
|
"description": "Shared host-neutral orchestration for Principles Disciple MVP-Core hook paths.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -21,7 +21,7 @@
|
|
|
21
21
|
"lint": "eslint \"src/**/*.ts\""
|
|
22
22
|
},
|
|
23
23
|
"dependencies": {
|
|
24
|
-
"@principles/core": "^1.287.
|
|
24
|
+
"@principles/core": "^1.287.6",
|
|
25
25
|
"@principles/install-layout": "^0.2.7",
|
|
26
26
|
"better-sqlite3": "^13.0.3",
|
|
27
27
|
"js-yaml": "^5.4.1"
|