@principles/host-runtime 0.10.4 → 0.12.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.
@@ -1,3 +1,4 @@
1
+ import { type PromptBudgetCapacitySource, type PromptBudgetCapacityStatus, type PromptBudgetFallbackReason, type PromptBudgetResolution, type PromptBudgetTierFit } from '@principles/core/runtime-v2';
1
2
  import { type PromptInjectionBudgetScope, type PromptInjectionRouteDecision, type PromptInjectionRouteName, type PromptInjectionTargetHost } from './prompt-injection-projection.js';
2
3
  /**
3
4
  * SPEC PD_PROMPT_CAPACITY_V1 R-A3: per-live-activation injection status for
@@ -9,27 +10,53 @@ import { type PromptInjectionBudgetScope, type PromptInjectionRouteDecision, typ
9
10
  * `route_unconfirmed` surfaces AC-01's honest unknown instead of a guess.
10
11
  * No compliance/遵守 counts are produced here — absence of injection logs is
11
12
  * NOT reported as "zero compliance" (AC-12 boundary).
13
+ *
14
+ * SPEC-PD-MODEL-AWARE-PROMPT-BUDGET-001: "oversized" now means the entry
15
+ * exceeds the MAXIMUM supported budget (never injectable on any supported
16
+ * model); a row that misses the CURRENT projection budget but fits within
17
+ * the supported [min, max] range is `model_dependent` — whether it injects
18
+ * depends on the runtime model's context window, which this surface cannot
19
+ * see. Budget figures come from the projection's resolution (legacy
20
+ * fallback when no runtime capacity is confirmed), never a bare constant.
12
21
  */
13
- export type PromptActivationInjectionStatusKind = 'injectable' | 'oversized' | 'window_excluded' | 'resolution_failed' | 'route_unconfirmed';
22
+ export type PromptActivationInjectionStatusKind = 'injectable' | 'oversized' | 'model_dependent' | 'window_excluded' | 'resolution_failed' | 'route_unconfirmed';
14
23
  export interface PromptActivationInjectionStatus {
15
24
  activationId: string;
16
25
  principleId?: string;
17
26
  status: PromptActivationInjectionStatusKind;
18
- /** Present for injectable/window_excluded/oversized under a confirmed route. */
27
+ /**
28
+ * Present for injectable/window_excluded/oversized/model_dependent under a
29
+ * confirmed route. For model_dependent rows it keeps the POSITIVE window
30
+ * fact: true = the entry fits THIS surface's (usually fallback) projection
31
+ * window, but still exceeds the measured floor — a smaller-context runtime
32
+ * model would exclude it.
33
+ */
19
34
  inCurrentWindow?: boolean;
20
35
  /** Present when the single-item cost was computable. */
21
36
  costChars?: number;
22
37
  budget?: number;
23
38
  budgetScope?: PromptInjectionBudgetScope;
24
39
  route?: PromptInjectionRouteName;
25
- /** Present for resolution_failed / route_unconfirmed (structured reason). */
40
+ /** Present for resolution_failed / route_unconfirmed / model_dependent (structured reason). */
26
41
  reason?: string;
27
- /** Present for route_unconfirmed: per-host single-item fit facts. */
42
+ /** Present for route_unconfirmed: per-host single-item tier fit facts. */
28
43
  perHostFits?: readonly {
29
44
  hostKind: PromptInjectionTargetHost;
30
- fits: boolean;
45
+ tierFit: PromptBudgetTierFit;
31
46
  }[];
32
47
  }
48
+ /**
49
+ * PRI-1017: provenance of the budget the rows were measured against, copied
50
+ * from the SAME resolution the projection ran under. Optional on every field
51
+ * so a caller can degrade without inventing a capacity fact.
52
+ */
53
+ export interface PromptActivationBudgetProvenance {
54
+ capacitySource?: PromptBudgetCapacitySource;
55
+ capacityStatus?: PromptBudgetCapacityStatus;
56
+ contextWindowTokens?: number;
57
+ policyVersion?: string;
58
+ fallbackReason?: PromptBudgetFallbackReason;
59
+ }
33
60
  export interface PromptActivationInjectionStatusReport {
34
61
  route: PromptInjectionRouteDecision;
35
62
  /** Empty when injection is globally unavailable (prompt flag off / state.db missing) — see `globalReason`. */
@@ -37,8 +64,23 @@ export interface PromptActivationInjectionStatusReport {
37
64
  warnings: string[];
38
65
  /** Present when no status could be computed at all (fail-loud degradation, rc-9). */
39
66
  globalReason?: string;
67
+ /** Present for a confirmed route: the budget basis every row was measured under. */
68
+ budgetProvenance?: PromptActivationBudgetProvenance;
40
69
  }
41
70
  export declare function readPromptActivationInjectionStatuses(input: {
42
71
  workspaceDir: string;
43
72
  targetHost?: PromptInjectionTargetHost;
73
+ /**
74
+ * SPEC-PD-MODEL-AWARE-PROMPT-BUDGET-001 §6.1 / PRI-1017: budget resolution
75
+ * the status projection must run under — console callers pass the
76
+ * resolution rebuilt from the workspace's most recent runtime budget
77
+ * observation. Absent → the honest fixed-fallback default.
78
+ */
79
+ promptBudget?: PromptBudgetResolution;
80
+ /**
81
+ * PRI-1017: per-host observations for a route confirmed by WORKSPACE FACTS
82
+ * (no request-level targetHost). The projection uses only the resolved
83
+ * host's own budget; an unidentified shared route stays on the fallback.
84
+ */
85
+ perHostPromptBudget?: Partial<Record<PromptInjectionTargetHost, PromptBudgetResolution>>;
44
86
  }): Promise<PromptActivationInjectionStatusReport>;
@@ -1,6 +1,6 @@
1
1
  import fs from 'node:fs';
2
2
  import path from 'node:path';
3
- import { RUNTIME_V2_PRINCIPLE_BUDGET, SqliteActivationStateStore, SqliteConnection, SqlitePIArtifactStore, computeFeatureFlagsFromConfig, filterPromptActivations, resolvePrincipleFromArtifact, } from '@principles/core/runtime-v2';
3
+ import { PROMPT_BUDGET_MAX_CHARS, PROMPT_BUDGET_MIN_CHARS, SqliteActivationStateStore, SqliteConnection, SqlitePIArtifactStore, classifySingleItemBudgetTier, computeFeatureFlagsFromConfig, filterPromptActivations, resolvePrincipleFromArtifact, } from '@principles/core/runtime-v2';
4
4
  import { loadPdConfigForPlugin } from './pd-config.js';
5
5
  import { computePromptSingleItemFit } from './prompt-activation-deliverability.js';
6
6
  import { PROMPT_INJECTION_TARGET_HOSTS, buildLivePromptInjectionProjection, promptRouteForHost, resolvePromptInjectionRouteDecision, } from './prompt-injection-projection.js';
@@ -86,12 +86,12 @@ export async function readPromptActivationInjectionStatuses(input) {
86
86
  }
87
87
  const perHostFits = PROMPT_INJECTION_TARGET_HOSTS.map((hostKind) => {
88
88
  const hostRoute = promptRouteForHost(hostKind, route.flagEnabled);
89
- const { fits } = computePromptSingleItemFit({
89
+ const { costChars } = computePromptSingleItemFit({
90
90
  principle: { principleId: row.principleId, text: row.text, artifactId: '', activationId: row.activationId },
91
91
  route: hostRoute,
92
92
  selfReportEnabled,
93
93
  });
94
- return { hostKind, fits };
94
+ return { hostKind, tierFit: classifySingleItemBudgetTier(costChars) };
95
95
  });
96
96
  return {
97
97
  activationId: row.activationId,
@@ -103,9 +103,12 @@ export async function readPromptActivationInjectionStatuses(input) {
103
103
  });
104
104
  return { route, statuses, warnings };
105
105
  }
106
+ const decidedBudget = input.promptBudget
107
+ ?? (route.hostKind !== undefined ? input.perHostPromptBudget?.[route.hostKind] : undefined);
106
108
  const projection = await buildLivePromptInjectionProjection({
107
109
  workspaceDir: input.workspaceDir,
108
110
  targetHost: route.hostKind,
111
+ ...(decidedBudget !== undefined ? { promptBudget: decidedBudget } : {}),
109
112
  });
110
113
  warnings.push(...projection.warnings);
111
114
  const statuses = rows.map((row) => {
@@ -116,20 +119,43 @@ export async function readPromptActivationInjectionStatuses(input) {
116
119
  principle: { principleId: row.principleId, text: row.text, artifactId: '', activationId: row.activationId },
117
120
  route: projection.route,
118
121
  selfReportEnabled,
122
+ budget: projection.budget,
119
123
  });
120
124
  const inCurrentWindow = projection.injectedActivationIds.includes(row.activationId);
121
125
  const budgetScope = projection.budgetScope;
122
- if (!fits) {
126
+ const serialization = projection.route === 'shared_render' ? 'full directive render' : 'list entry';
127
+ // Tier verdict FIRST (SPEC §6.3): the projection budget on this surface
128
+ // is the fixed fallback (2000) unless the caller passed a runtime budget
129
+ // OBSERVATION (PRI-1017) — and even then an entry in (MIN, fallback]
130
+ // exceeds the measured floor, so on a 32K/64K model (budget = floor) it
131
+ // would NOT inject. Reporting it as an unconditional `injectable` would
132
+ // contradict the approval-time `model_dependent` verdict for the very
133
+ // same artifact.
134
+ const tierFit = classifySingleItemBudgetTier(costChars);
135
+ if (tierFit === 'exceeds_max_tier') {
123
136
  return {
124
137
  activationId: row.activationId,
125
138
  principleId: row.principleId,
126
139
  status: 'oversized',
127
140
  inCurrentWindow: false,
128
141
  costChars,
129
- budget: RUNTIME_V2_PRINCIPLE_BUDGET,
142
+ budget: projection.budget,
130
143
  budgetScope,
131
144
  route: projection.route,
132
- reason: `single_item_exceeds_budget: this principle's own entry is ${costChars} chars > ${RUNTIME_V2_PRINCIPLE_BUDGET} under the ${projection.route === 'shared_render' ? 'full directive render' : 'list entry'} serialization`,
145
+ reason: `single_item_exceeds_budget: this principle's own entry is ${costChars} chars > the ${PROMPT_BUDGET_MAX_CHARS}-char maximum supported budget under the ${serialization} serialization`,
146
+ };
147
+ }
148
+ if (tierFit === 'model_dependent') {
149
+ return {
150
+ activationId: row.activationId,
151
+ principleId: row.principleId,
152
+ status: 'model_dependent',
153
+ inCurrentWindow,
154
+ costChars,
155
+ budget: projection.budget,
156
+ budgetScope,
157
+ route: projection.route,
158
+ reason: `model_dependent_capacity: this principle's own entry is ${costChars} chars — above the ${PROMPT_BUDGET_MIN_CHARS}-char measured floor, so smaller-context models exclude it${fits ? ' even though it fits this surface\'s fallback-budget estimate' : ''}; injection depends on the runtime model`,
133
159
  };
134
160
  }
135
161
  return {
@@ -138,10 +164,21 @@ export async function readPromptActivationInjectionStatuses(input) {
138
164
  status: inCurrentWindow ? 'injectable' : 'window_excluded',
139
165
  inCurrentWindow,
140
166
  costChars,
141
- budget: RUNTIME_V2_PRINCIPLE_BUDGET,
167
+ budget: projection.budget,
142
168
  budgetScope,
143
169
  route: projection.route,
144
170
  };
145
171
  });
146
- return { route, statuses, warnings };
172
+ return {
173
+ route,
174
+ statuses,
175
+ warnings,
176
+ budgetProvenance: {
177
+ ...(projection.budgetCapacitySource !== undefined ? { capacitySource: projection.budgetCapacitySource } : {}),
178
+ ...(projection.budgetCapacityStatus !== undefined ? { capacityStatus: projection.budgetCapacityStatus } : {}),
179
+ ...(typeof projection.budgetContextWindowTokens === 'number' ? { contextWindowTokens: projection.budgetContextWindowTokens } : {}),
180
+ ...(projection.budgetPolicyVersion !== undefined ? { policyVersion: projection.budgetPolicyVersion } : {}),
181
+ ...(projection.budgetFallbackReason !== undefined ? { fallbackReason: projection.budgetFallbackReason } : {}),
182
+ },
183
+ };
147
184
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@principles/host-runtime",
3
- "version": "0.10.4",
3
+ "version": "0.12.0",
4
4
  "description": "Shared host-neutral orchestration for Principles Disciple MVP-Core hook paths.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -31,7 +31,7 @@
31
31
  "lint": "eslint \"src/**/*.ts\""
32
32
  },
33
33
  "dependencies": {
34
- "@principles/core": "^1.294.1",
34
+ "@principles/core": "^1.295.0",
35
35
  "@principles/install-layout": "^0.4.0",
36
36
  "better-sqlite3": "^13.0.3",
37
37
  "js-yaml": "^5.4.3"