@principles/host-runtime 0.2.0 → 0.2.1
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.
|
@@ -342,9 +342,9 @@ function insertCorrectionPain({ db, candidate, workspaceDir, painId, detection,
|
|
|
342
342
|
// is NOT used here: buildEvidence already truncated it upstream, so a token
|
|
343
343
|
// straddling that cut would survive sanitization the same way.
|
|
344
344
|
const excerpt = sanitizeString(candidate.text, workspaceDir);
|
|
345
|
-
db.prepare(`INSERT INTO pain_events (session_id, source, score, reason, severity, origin, confidence, text, canonical_pain_id, runtime_task_id, created_at)
|
|
346
|
-
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`)
|
|
347
|
-
.run(candidate.rootSessionId, 'user_correction', GOVERNANCE_STRONG_PAIN_SCORE, reason.slice(0, MAX_REASON_BOUND), 'severe', 'system_infer', null, excerpt, painId, null, nowIso);
|
|
345
|
+
db.prepare(`INSERT INTO pain_events (session_id, source, score, reason, severity, origin, confidence, text, canonical_pain_id, runtime_task_id, host_kind, created_at)
|
|
346
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`)
|
|
347
|
+
.run(candidate.rootSessionId, 'user_correction', GOVERNANCE_STRONG_PAIN_SCORE, reason.slice(0, MAX_REASON_BOUND), 'severe', 'system_infer', null, excerpt, painId, null, candidate.hostKind, nowIso);
|
|
348
348
|
return reason;
|
|
349
349
|
}
|
|
350
350
|
function taskSubmissionPayload(candidate, workspaceDir, reason) {
|
|
@@ -415,6 +415,9 @@ function admitCorrection({ db, candidate, workspaceDir, now, nowIso, keywordStor
|
|
|
415
415
|
// Live + transcript duplicate or a crash-replay: the canonical pain
|
|
416
416
|
// already exists — no second row, no second quota consumption.
|
|
417
417
|
duplicate = true;
|
|
418
|
+
// PRI-640 §17: enrich unknown → codex from proven governance lineage;
|
|
419
|
+
// the IS NULL guard keeps any durable attribution (never overwrites).
|
|
420
|
+
db.prepare('UPDATE pain_events SET host_kind = ? WHERE canonical_pain_id = ? AND host_kind IS NULL').run(candidate.hostKind, painId);
|
|
418
421
|
}
|
|
419
422
|
else {
|
|
420
423
|
if (!tryConsumeRateLimit({ db, rootSessionId: candidate.rootSessionId, ruleVersion: detection.ruleVersion, now })) {
|
|
@@ -488,9 +491,12 @@ function admitToolFailure({ db, candidate, workspaceDir, nowIso }) {
|
|
|
488
491
|
db.prepare(`INSERT INTO tool_calls (session_id, tool_name, outcome, duration_ms, exit_code, error_type, error_message, gfi_before, gfi_after, params_json, result_preview, created_at) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`)
|
|
489
492
|
.run(candidate.rootSessionId, candidate.toolName, outcome.failure ? 'failure' : 'success', outcome.durationMs ?? null, outcome.exitCode, outcome.error ? outcome.error.split(/[\s:]/, 1)[0] : null, outcome.error ?? null, null, null, paramsJson, resultPreview, nowIso);
|
|
490
493
|
reason = `tool=${candidate.toolName}; error=${outcome.error ?? `exit=${outcome.exitCode}`}; path=${relativePath}`;
|
|
491
|
-
db.prepare(`INSERT INTO pain_events (session_id, source, score, reason, severity, origin, confidence, text, canonical_pain_id, runtime_task_id, created_at) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`)
|
|
492
|
-
.run(candidate.rootSessionId, sourceObservation.failureSource ?? 'tool_failure', painScore, reason, painScore >= 70 ? 'severe' : painScore >= 40 ? 'moderate' : 'mild', 'system_infer', null, null, painId, null, nowIso);
|
|
494
|
+
db.prepare(`INSERT INTO pain_events (session_id, source, score, reason, severity, origin, confidence, text, canonical_pain_id, runtime_task_id, host_kind, created_at) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`)
|
|
495
|
+
.run(candidate.rootSessionId, sourceObservation.failureSource ?? 'tool_failure', painScore, reason, painScore >= 70 ? 'severe' : painScore >= 40 ? 'moderate' : 'mild', 'system_infer', null, null, painId, null, candidate.hostKind, nowIso);
|
|
493
496
|
}
|
|
497
|
+
// PRI-640 §17: enrich unknown → codex on the already-admitted duplicate
|
|
498
|
+
// pain row; the IS NULL guard keeps any durable attribution.
|
|
499
|
+
db.prepare('UPDATE pain_events SET host_kind = ? WHERE canonical_pain_id = ? AND host_kind IS NULL').run(candidate.hostKind, painId);
|
|
494
500
|
if (!duplicateWithoutPain) {
|
|
495
501
|
insertAdmissionMarker({ db, candidate, workspaceDir, canonicalPainId: painId, ruleVersion: null, reason: sanitizeString(reason, workspaceDir), nowIso });
|
|
496
502
|
}
|
package/dist/index.d.ts
CHANGED
|
@@ -3,6 +3,7 @@ import { buildActivePrinciplePromptContext } from './active-principle-prompt.js'
|
|
|
3
3
|
import { type RuleContextProvider, type RuleInputEnrichmentProvider } from './production-rulehost-gate.js';
|
|
4
4
|
import type { RuleImplementationRuntime } from './rule-implementation-runtime.js';
|
|
5
5
|
import { type PainDatabaseFactory, type PainEnrichmentProvider } from './production-pain-evidence.js';
|
|
6
|
+
import type { GovernanceHostKind } from '@principles/core/runtime-v2';
|
|
6
7
|
export * from './active-principle-prompt.js';
|
|
7
8
|
export * from './pd-config.js';
|
|
8
9
|
export * from './production-rulehost-gate.js';
|
|
@@ -53,4 +54,6 @@ export declare function createProductionHostRuntime(options?: Partial<Pick<HostR
|
|
|
53
54
|
ruleImplementationRuntime?: RuleImplementationRuntime;
|
|
54
55
|
painEnrichmentProvider?: PainEnrichmentProvider;
|
|
55
56
|
painDatabaseFactory?: PainDatabaseFactory;
|
|
57
|
+
/** PRI-640: host attribution supplied by the constructing host adapter (OpenClaw / Codex). */
|
|
58
|
+
hostKind?: GovernanceHostKind;
|
|
56
59
|
}): HostRuntime;
|
package/dist/index.js
CHANGED
|
@@ -146,6 +146,7 @@ export function createProductionHostRuntime(options = {}) {
|
|
|
146
146
|
afterToolCall: options.afterToolCall ?? createProductionPainEvidenceHandler({
|
|
147
147
|
...(options.painEnrichmentProvider ? { painEnrichmentProvider: options.painEnrichmentProvider } : {}),
|
|
148
148
|
...(options.painDatabaseFactory ? { painDatabaseFactory: options.painDatabaseFactory } : {}),
|
|
149
|
+
...(options.hostKind ? { hostKind: options.hostKind } : {}),
|
|
149
150
|
}),
|
|
150
151
|
beforeToolCall: options.beforeToolCall ?? productionGate,
|
|
151
152
|
async beforePromptBuild(event) {
|
|
@@ -170,6 +170,20 @@ export async function runInternalizationConsumerCycle(workspaceDir, ports) {
|
|
|
170
170
|
}
|
|
171
171
|
const runtimeConfigResult = resolveRuntimeConfigFromPdConfig(configResult.effective, (name) => envGetter(name));
|
|
172
172
|
if (isRuntimeConfigError(runtimeConfigResult)) {
|
|
173
|
+
// PRI-638 P1-C: the shared runtime resolver reads the Diagnostician
|
|
174
|
+
// binding; an Owner-disabled agent reports reason='disabled'. That is an
|
|
175
|
+
// intentional governance pause, not a broken runtime — report it
|
|
176
|
+
// distinctly so consumers (Codex worker) never treat it as degradation.
|
|
177
|
+
if (runtimeConfigResult.reason === 'disabled') {
|
|
178
|
+
const disabledInfo = JSON.stringify({
|
|
179
|
+
reason: 'capability_disabled',
|
|
180
|
+
message: runtimeConfigResult.message,
|
|
181
|
+
nextAction: runtimeConfigResult.nextAction,
|
|
182
|
+
});
|
|
183
|
+
emitEvent('INTERNALIZATION_CONSUMER_SKIP', disabledInfo);
|
|
184
|
+
logger.warn(`[PD:${logLabel}] Cycle skipped: Diagnostician capability disabled — ${runtimeConfigResult.message}`);
|
|
185
|
+
return { ran: false, skipReason: 'capability_disabled' };
|
|
186
|
+
}
|
|
173
187
|
const rtInfo = JSON.stringify({
|
|
174
188
|
reason: 'runtime_config_error',
|
|
175
189
|
message: runtimeConfigResult.message,
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import Database from 'better-sqlite3';
|
|
2
|
+
import type { GovernanceHostKind } from '@principles/core/runtime-v2';
|
|
2
3
|
import type { HostEvent, HostEventResult } from '@principles/core/host';
|
|
3
4
|
/** Shared with the governance admission path so both gate tool failures identically. */
|
|
4
5
|
export declare const PRODUCTION_WRITE_TOOLS: ReadonlySet<string>;
|
|
@@ -75,9 +76,15 @@ export declare function deriveProductionCorrectionPainIdentity(fields: {
|
|
|
75
76
|
painId: string;
|
|
76
77
|
};
|
|
77
78
|
declare function hasCanonicalSchema(db: Database.Database): boolean;
|
|
79
|
+
/**
|
|
80
|
+
* PRI-640: host attribution is supplied by the host adapter that constructs
|
|
81
|
+
* this shared handler (OpenClaw / Codex adapters) — never guessed inside the
|
|
82
|
+
* writer. Omitted -> NULL (unknown).
|
|
83
|
+
*/
|
|
78
84
|
export declare function createProductionPainEvidenceHandler(options?: {
|
|
79
85
|
painEnrichmentProvider?: PainEnrichmentProvider;
|
|
80
86
|
painDatabaseFactory?: PainDatabaseFactory;
|
|
87
|
+
hostKind?: GovernanceHostKind;
|
|
81
88
|
}): (event: HostEvent) => Promise<HostEventResult>;
|
|
82
89
|
export declare function resetProductionPainCooldownForTest(): void;
|
|
83
90
|
export declare function productionPainCooldownEntryCountForTest(): number;
|
|
@@ -173,7 +173,7 @@ export function deriveProductionCorrectionPainIdentity(fields) {
|
|
|
173
173
|
const REQUIRED_COLUMNS = {
|
|
174
174
|
sessions: { session_id: 'TEXT', started_at: 'TEXT', updated_at: 'TEXT' },
|
|
175
175
|
tool_calls: { session_id: 'TEXT', tool_name: 'TEXT', outcome: 'TEXT', duration_ms: 'INTEGER', exit_code: 'INTEGER', error_type: 'TEXT', error_message: 'TEXT', gfi_before: 'REAL', gfi_after: 'REAL', params_json: 'TEXT', result_preview: 'TEXT', created_at: 'TEXT' },
|
|
176
|
-
pain_events: { session_id: 'TEXT', source: 'TEXT', score: 'REAL', reason: 'TEXT', severity: 'TEXT', origin: 'TEXT', confidence: 'REAL', text: 'TEXT', canonical_pain_id: 'TEXT', runtime_task_id: 'TEXT', created_at: 'TEXT' },
|
|
176
|
+
pain_events: { session_id: 'TEXT', source: 'TEXT', score: 'REAL', reason: 'TEXT', severity: 'TEXT', origin: 'TEXT', confidence: 'REAL', text: 'TEXT', canonical_pain_id: 'TEXT', runtime_task_id: 'TEXT', host_kind: 'TEXT', created_at: 'TEXT' },
|
|
177
177
|
};
|
|
178
178
|
function pragmaField(row, key) {
|
|
179
179
|
return isRecord(row) && Object.hasOwn(row, key) ? Object.getOwnPropertyDescriptor(row, key)?.value : undefined;
|
|
@@ -224,9 +224,14 @@ function hasCanonicalSchema(db) {
|
|
|
224
224
|
db.prepare('SELECT 1 FROM pain_events WHERE canonical_pain_id = ?');
|
|
225
225
|
db.prepare('INSERT INTO sessions (session_id, started_at, updated_at) VALUES (?, ?, ?) ON CONFLICT(session_id) DO UPDATE SET updated_at = excluded.updated_at');
|
|
226
226
|
db.prepare('INSERT INTO tool_calls (session_id, tool_name, outcome, duration_ms, exit_code, error_type, error_message, gfi_before, gfi_after, params_json, result_preview, created_at) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)');
|
|
227
|
-
db.prepare('INSERT INTO pain_events (session_id, source, score, reason, severity, origin, confidence, text, canonical_pain_id, runtime_task_id, created_at) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)');
|
|
227
|
+
db.prepare('INSERT INTO pain_events (session_id, source, score, reason, severity, origin, confidence, text, canonical_pain_id, runtime_task_id, host_kind, created_at) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)');
|
|
228
228
|
return true;
|
|
229
229
|
}
|
|
230
|
+
/**
|
|
231
|
+
* PRI-640: host attribution is supplied by the host adapter that constructs
|
|
232
|
+
* this shared handler (OpenClaw / Codex adapters) — never guessed inside the
|
|
233
|
+
* writer. Omitted -> NULL (unknown).
|
|
234
|
+
*/
|
|
230
235
|
export function createProductionPainEvidenceHandler(options = {}) {
|
|
231
236
|
return async (event) => {
|
|
232
237
|
const dbPath = path.join(event.context.workspaceDir, '.state', 'trajectory.db');
|
|
@@ -319,8 +324,8 @@ export function createProductionPainEvidenceHandler(options = {}) {
|
|
|
319
324
|
// declared enum (event-types.ts: who reported the pain), not an
|
|
320
325
|
// attribution claim.
|
|
321
326
|
const reason = `tool=${toolName}; error=${outcome.error ?? `exit=${outcome.exitCode}`}; path=${relativePath}`;
|
|
322
|
-
db.prepare(`INSERT INTO pain_events (session_id, source, score, reason, severity, origin, confidence, text, canonical_pain_id, runtime_task_id, created_at) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`)
|
|
323
|
-
.run(event.context.sessionId, sourceObservation.failureSource ?? 'tool_failure', painScore, reason, painScore >= 70 ? 'severe' : painScore >= 40 ? 'moderate' : 'mild', 'system_infer', null, enrichment.evidence?.map((entry) => `${entry.sourceRef}: ${entry.note}`).join('\n') ?? null, painId, null, createdAt);
|
|
327
|
+
db.prepare(`INSERT INTO pain_events (session_id, source, score, reason, severity, origin, confidence, text, canonical_pain_id, runtime_task_id, host_kind, created_at) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`)
|
|
328
|
+
.run(event.context.sessionId, sourceObservation.failureSource ?? 'tool_failure', painScore, reason, painScore >= 70 ? 'severe' : painScore >= 40 ? 'moderate' : 'mild', 'system_infer', null, enrichment.evidence?.map((entry) => `${entry.sourceRef}: ${entry.note}`).join('\n') ?? null, painId, null, options.hostKind ?? null, createdAt);
|
|
324
329
|
}
|
|
325
330
|
})();
|
|
326
331
|
if (admitted && !duplicate) {
|