@principles/host-runtime 0.3.11 → 0.4.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.
|
@@ -46,6 +46,16 @@ export interface EvaluatorRuntimeContextInput {
|
|
|
46
46
|
export type EvaluatorRuntimeContextResolution = {
|
|
47
47
|
readonly ok: true;
|
|
48
48
|
readonly gateDeps: RefinerRuleHostGateDeps;
|
|
49
|
+
/**
|
|
50
|
+
* PRI-741: the registry snapshot the gateDeps were built from. Callers
|
|
51
|
+
* that need the host tool list (e.g. the host-alias parity projection)
|
|
52
|
+
* MUST derive it from HERE — a second independent
|
|
53
|
+
* `resolveWorkspaceHostToolSemantics` read could observe a different
|
|
54
|
+
* declaration snapshot than the replay gate.
|
|
55
|
+
*/
|
|
56
|
+
readonly registry: ToolSemanticRegistry;
|
|
57
|
+
/** Host kind label(s) when known; the host-threaded path has none. */
|
|
58
|
+
readonly hostKinds?: readonly string[];
|
|
49
59
|
} | {
|
|
50
60
|
readonly ok: false;
|
|
51
61
|
readonly reason: string;
|
|
@@ -47,6 +47,7 @@ export function createEvaluatorRuntimeContext(input) {
|
|
|
47
47
|
if (input.toolSemantics) {
|
|
48
48
|
return {
|
|
49
49
|
ok: true,
|
|
50
|
+
registry: input.toolSemantics,
|
|
50
51
|
gateDeps: createProductionGateDeps({ projectDir: input.workspaceDir, toolSemantics: input.toolSemantics }),
|
|
51
52
|
};
|
|
52
53
|
}
|
|
@@ -56,6 +57,8 @@ export function createEvaluatorRuntimeContext(input) {
|
|
|
56
57
|
}
|
|
57
58
|
return {
|
|
58
59
|
ok: true,
|
|
60
|
+
registry: resolved.registry,
|
|
61
|
+
hostKinds: resolved.hostKinds,
|
|
59
62
|
gateDeps: createProductionGateDeps({ projectDir: input.workspaceDir, toolSemantics: resolved.registry }),
|
|
60
63
|
};
|
|
61
64
|
}
|
|
@@ -32,6 +32,12 @@ export interface InternalizationConsumerCyclePorts {
|
|
|
32
32
|
* hosts that have not declared semantics keep core-baseline behavior.
|
|
33
33
|
*/
|
|
34
34
|
readonly toolSemantics?: ToolSemanticRegistry;
|
|
35
|
+
/**
|
|
36
|
+
* PRI-741: host kind label(s) for the artificer's HOST SEMANTIC CONTEXT
|
|
37
|
+
* block (e.g. ['openclaw']). Only meaningful together with `toolSemantics`;
|
|
38
|
+
* omitted → the artificer prompt is built without host context.
|
|
39
|
+
*/
|
|
40
|
+
readonly hostKinds?: readonly string[];
|
|
35
41
|
/** Log label used in human log prefixes ('AutoConsumer' | 'CodexWorker' | …). */
|
|
36
42
|
readonly logLabel: string;
|
|
37
43
|
readonly envGetter?: (name: string) => string | undefined;
|
|
@@ -21,7 +21,7 @@
|
|
|
21
21
|
* env getter. Everything else is the existing production path.
|
|
22
22
|
*/
|
|
23
23
|
import { createHash } from 'node:crypto';
|
|
24
|
-
import { createRuntimeStateHandle, InternalizationOrchestrator, DreamerRunner, PhilosopherRunner, ScribeRunner, ArtificerRunner, EvaluatorRunner, RolloutReviewerRunner, DefaultDreamerValidator, DefaultPhilosopherValidator, DefaultScribeValidator, DefaultArtificerValidator, DefaultEvaluatorValidator, DefaultRolloutReviewerValidator, PiAiRuntimeAdapter, L2AgentLoopAdapter, buildL2PrincipleReaderFromLedger, OpenClawCliRuntimeAdapter, storeEmitter, createProductionGateDeps, resolveRuntimeConfigFromPdConfig, isRuntimeConfigError, computeConsumerDecision, FULL_CHAIN_CONSUMER_RUNNER_KINDS, DEFAULT_CONSUMER_RUNNER_KINDS, InternalizationQueueReadModel, MVP_CORE_TASK_KINDS, SqliteConnection, SqliteReconciliationCursorStore, SUCCEEDED_TRANSITIONS_SCOPE,
|
|
24
|
+
import { createRuntimeStateHandle, InternalizationOrchestrator, DreamerRunner, PhilosopherRunner, ScribeRunner, ArtificerRunner, buildArtificerHostSemanticContext, EvaluatorRunner, RolloutReviewerRunner, DefaultDreamerValidator, DefaultPhilosopherValidator, DefaultScribeValidator, DefaultArtificerValidator, DefaultEvaluatorValidator, DefaultRolloutReviewerValidator, PiAiRuntimeAdapter, L2AgentLoopAdapter, buildL2PrincipleReaderFromLedger, OpenClawCliRuntimeAdapter, storeEmitter, createProductionGateDeps, resolveRuntimeConfigFromPdConfig, resolveRuntimeConfigForAgent, AGENT_NAME_FOR_TASK_KIND, isRuntimeConfigError, computeConsumerDecision, FULL_CHAIN_CONSUMER_RUNNER_KINDS, DEFAULT_CONSUMER_RUNNER_KINDS, InternalizationQueueReadModel, MVP_CORE_TASK_KINDS, SqliteConnection, SqliteReconciliationCursorStore, SUCCEEDED_TRANSITIONS_SCOPE,
|
|
25
25
|
// PRI-714: resolve outputLanguage from the same effective config the
|
|
26
26
|
// runners already receive (EP-07: canonical resolved value, not raw input).
|
|
27
27
|
resolveOutputLanguage, } from '@principles/core/runtime-v2';
|
|
@@ -144,6 +144,13 @@ async function runReconciliationBudget(workspaceDir, orchestrator, ports) {
|
|
|
144
144
|
export async function runInternalizationConsumerCycle(workspaceDir, ports) {
|
|
145
145
|
const { logger, emitEvent, logLabel, owner } = ports;
|
|
146
146
|
const { hostToolCatalog, toolSemantics } = ports;
|
|
147
|
+
// PRI-741: artificer host semantic projection — built once from the SAME
|
|
148
|
+
// registry the activation gate/replay use (ports.toolSemantics), so the
|
|
149
|
+
// generation prompt teaches exactly the host dispatch surface that
|
|
150
|
+
// validateRuleReliability will later enforce. Sanitized for prompt use.
|
|
151
|
+
const artificerHostSemanticContext = toolSemantics !== undefined
|
|
152
|
+
? buildArtificerHostSemanticContext(toolSemantics, ports.hostKinds ?? [])
|
|
153
|
+
: undefined;
|
|
147
154
|
const envGetter = ports.envGetter ?? ((name) => process.env[name]);
|
|
148
155
|
let orchestrator = null;
|
|
149
156
|
const flag = loadFeatureFlagFromConfig(workspaceDir, INTERNALIZATION_AUTO_CONSUMER_FLAG_ID, {
|
|
@@ -240,13 +247,50 @@ export async function runInternalizationConsumerCycle(workspaceDir, ports) {
|
|
|
240
247
|
// Advance the configured runner kinds in priority order (dreamer first,
|
|
241
248
|
// then philosopher→…→rollout_reviewer under full-chain scope). Lease the
|
|
242
249
|
// first ready task whose dependencies are satisfied.
|
|
250
|
+
//
|
|
251
|
+
// PRI-719 (EP002-R2 F4): the runtime profile is resolved PER KIND, before
|
|
252
|
+
// wakeOnce — `internalAgents.agents[kind].runtimeProfile` (fallback
|
|
253
|
+
// defaultRuntime) now actually governs the stage that runs on it. Before,
|
|
254
|
+
// the whole chain shared the diagnostician binding and every per-agent
|
|
255
|
+
// profile declaration was silently ignored. A kind whose profile cannot
|
|
256
|
+
// resolve is skipped observably (no lease taken for it).
|
|
243
257
|
let wakeResult = null;
|
|
244
258
|
let lastSkipDecision = 'no_ready_tasks';
|
|
245
259
|
let lastSkipReason;
|
|
260
|
+
let taskRuntimeConfig = null;
|
|
246
261
|
for (const kind of decision.runnerKinds) {
|
|
262
|
+
const agentName = AGENT_NAME_FOR_TASK_KIND[kind];
|
|
263
|
+
if (agentName === undefined) {
|
|
264
|
+
lastSkipDecision = 'runtime_config_error';
|
|
265
|
+
lastSkipReason = `no internal agent mapping for task kind '${kind}'`;
|
|
266
|
+
continue;
|
|
267
|
+
}
|
|
268
|
+
const kindRuntime = resolveRuntimeConfigForAgent(configResult.effective, agentName, {
|
|
269
|
+
getEnvVar: (name) => envGetter(name),
|
|
270
|
+
// Consumer stage scope is the internalization_full_chain flag, not
|
|
271
|
+
// agents[kind].enabled (the shipped default disables
|
|
272
|
+
// philosopher/evaluator/rolloutReviewer yet full-chain runs them) —
|
|
273
|
+
// see ResolveRuntimeConfigForAgentOptions.
|
|
274
|
+
ignoreAgentEnabled: true,
|
|
275
|
+
});
|
|
276
|
+
if (isRuntimeConfigError(kindRuntime)) {
|
|
277
|
+
// rc-9: the degradation is observable per kind; other kinds still run.
|
|
278
|
+
emitEvent('INTERNALIZATION_CONSUMER_SKIP', JSON.stringify({
|
|
279
|
+
reason: 'runtime_config_error',
|
|
280
|
+
taskKind: kind,
|
|
281
|
+
agentName,
|
|
282
|
+
message: kindRuntime.message,
|
|
283
|
+
nextAction: kindRuntime.nextAction,
|
|
284
|
+
}));
|
|
285
|
+
logger.warn(`[PD:${logLabel}] Runtime config error for ${kind} (agent ${agentName}): ${kindRuntime.message}`);
|
|
286
|
+
lastSkipDecision = 'runtime_config_error';
|
|
287
|
+
lastSkipReason = kindRuntime.message;
|
|
288
|
+
continue;
|
|
289
|
+
}
|
|
247
290
|
const candidate = await orchestrator.wakeOnce(kind);
|
|
248
291
|
if (candidate.decision === 'would_lease') {
|
|
249
292
|
wakeResult = candidate;
|
|
293
|
+
taskRuntimeConfig = kindRuntime;
|
|
250
294
|
break;
|
|
251
295
|
}
|
|
252
296
|
// Keep decision/reason paired across iterations so the final SKIP log
|
|
@@ -254,7 +298,7 @@ export async function runInternalizationConsumerCycle(workspaceDir, ports) {
|
|
|
254
298
|
lastSkipDecision = candidate.decision;
|
|
255
299
|
lastSkipReason = candidate.decision === 'no_ready_tasks' ? candidate.reason : undefined;
|
|
256
300
|
}
|
|
257
|
-
if (!wakeResult) {
|
|
301
|
+
if (!wakeResult || taskRuntimeConfig === null) {
|
|
258
302
|
// A: 预算由周期 finally 统一执行 (每周期恰一次,含 backlog 场景)
|
|
259
303
|
const skipPayload = { decision: lastSkipDecision };
|
|
260
304
|
if (lastSkipReason) {
|
|
@@ -264,8 +308,11 @@ export async function runInternalizationConsumerCycle(workspaceDir, ports) {
|
|
|
264
308
|
logger.info(`[PD:${logLabel}] No task to consume: ${lastSkipDecision}`);
|
|
265
309
|
return { ran: false, skipReason: lastSkipDecision };
|
|
266
310
|
}
|
|
311
|
+
// PRI-719: everything downstream reads the LEASED TASK's resolved config —
|
|
312
|
+
// the kind-specific profile, not the shared gate resolution.
|
|
313
|
+
const taskRuntimeKind = taskRuntimeConfig.runtimeKind;
|
|
267
314
|
let adapter;
|
|
268
|
-
if (
|
|
315
|
+
if (taskRuntimeKind === 'pi-ai') {
|
|
269
316
|
// PRI-419: when l2_dreamer flag is on AND this is a dreamer task, route
|
|
270
317
|
// through the L2 multi-turn agent loop. Non-dreamer runners always use PiAi.
|
|
271
318
|
const l2Flag = loadFeatureFlagFromConfig(workspaceDir, 'l2_dreamer');
|
|
@@ -275,14 +322,14 @@ export async function runInternalizationConsumerCycle(workspaceDir, ports) {
|
|
|
275
322
|
logger: { warn: (msg) => logger.warn(msg) },
|
|
276
323
|
});
|
|
277
324
|
adapter = new L2AgentLoopAdapter({
|
|
278
|
-
provider:
|
|
279
|
-
model:
|
|
280
|
-
apiKeyEnv:
|
|
281
|
-
baseUrl:
|
|
325
|
+
provider: taskRuntimeConfig.provider ?? 'openai',
|
|
326
|
+
model: taskRuntimeConfig.model ?? 'gpt-4o',
|
|
327
|
+
apiKeyEnv: taskRuntimeConfig.apiKeyEnv ?? 'OPENAI_API_KEY',
|
|
328
|
+
baseUrl: taskRuntimeConfig.baseUrl,
|
|
282
329
|
workspace: workspaceDir,
|
|
283
|
-
totalBudgetMs:
|
|
330
|
+
totalBudgetMs: taskRuntimeConfig.timeoutMs,
|
|
284
331
|
// PRI-633: profile systemPrompt rides as the append layer.
|
|
285
|
-
...(
|
|
332
|
+
...(taskRuntimeConfig.systemPrompt ? { systemPrompt: taskRuntimeConfig.systemPrompt } : {}),
|
|
286
333
|
}, {
|
|
287
334
|
artifactReader: {
|
|
288
335
|
// Explicit adapter: PIArtifactRecord → PdL2ArtifactReader. The store returns
|
|
@@ -301,27 +348,27 @@ export async function runInternalizationConsumerCycle(workspaceDir, ports) {
|
|
|
301
348
|
}
|
|
302
349
|
else {
|
|
303
350
|
adapter = new PiAiRuntimeAdapter({
|
|
304
|
-
provider:
|
|
305
|
-
model:
|
|
306
|
-
apiKeyEnv:
|
|
307
|
-
maxRetries:
|
|
308
|
-
maxTokens:
|
|
309
|
-
timeoutMs:
|
|
310
|
-
baseUrl:
|
|
351
|
+
provider: taskRuntimeConfig.provider ?? 'openai',
|
|
352
|
+
model: taskRuntimeConfig.model ?? 'gpt-4o',
|
|
353
|
+
apiKeyEnv: taskRuntimeConfig.apiKeyEnv ?? 'OPENAI_API_KEY',
|
|
354
|
+
maxRetries: taskRuntimeConfig.maxRetries,
|
|
355
|
+
maxTokens: taskRuntimeConfig.maxTokens,
|
|
356
|
+
timeoutMs: taskRuntimeConfig.timeoutMs,
|
|
357
|
+
baseUrl: taskRuntimeConfig.baseUrl,
|
|
311
358
|
workspace: workspaceDir,
|
|
312
359
|
// PRI-633: profile systemPrompt rides as the append layer.
|
|
313
|
-
...(
|
|
360
|
+
...(taskRuntimeConfig.systemPrompt ? { systemPrompt: taskRuntimeConfig.systemPrompt } : {}),
|
|
314
361
|
});
|
|
315
362
|
}
|
|
316
363
|
}
|
|
317
|
-
else if (
|
|
364
|
+
else if (taskRuntimeKind === 'openclaw-cli') {
|
|
318
365
|
adapter = new OpenClawCliRuntimeAdapter({
|
|
319
|
-
runtimeMode:
|
|
366
|
+
runtimeMode: taskRuntimeConfig.openclawMode ?? 'default',
|
|
320
367
|
workspaceDir: workspaceDir,
|
|
321
368
|
});
|
|
322
369
|
}
|
|
323
370
|
else {
|
|
324
|
-
throw new Error(`Unsupported runtime kind resolved for auto-consumer: ${
|
|
371
|
+
throw new Error(`Unsupported runtime kind resolved for auto-consumer: ${taskRuntimeKind}`);
|
|
325
372
|
}
|
|
326
373
|
const { taskId } = wakeResult;
|
|
327
374
|
const { taskKind } = wakeResult;
|
|
@@ -340,9 +387,9 @@ export async function runInternalizationConsumerCycle(workspaceDir, ports) {
|
|
|
340
387
|
// same effective config (EP-07 canonical value).
|
|
341
388
|
const runnerOptions = {
|
|
342
389
|
owner,
|
|
343
|
-
runtimeKind,
|
|
390
|
+
runtimeKind: taskRuntimeKind,
|
|
344
391
|
effectiveConfig: configResult.effective,
|
|
345
|
-
timeoutMs:
|
|
392
|
+
timeoutMs: taskRuntimeConfig.timeoutMs,
|
|
346
393
|
outputLanguage: resolveOutputLanguage(configResult.effective.config.principles?.outputLanguage).outputLanguage,
|
|
347
394
|
};
|
|
348
395
|
// PRI-634 A3: workspace-scoped telemetry sink for the evaluator runner.
|
|
@@ -369,7 +416,9 @@ export async function runInternalizationConsumerCycle(workspaceDir, ports) {
|
|
|
369
416
|
runner = new ScribeRunner({ stateManager, runtimeAdapter: adapter, eventEmitter: storeEmitter, artifactStore: stateManager.piArtifactStore, validator: new DefaultScribeValidator(), contentHashFn }, runnerOptions);
|
|
370
417
|
break;
|
|
371
418
|
case 'artificer':
|
|
372
|
-
runner = new ArtificerRunner({ stateManager, runtimeAdapter: adapter, eventEmitter: storeEmitter, artifactStore: stateManager.piArtifactStore, validator: new DefaultArtificerValidator(), contentHashFn },
|
|
419
|
+
runner = new ArtificerRunner({ stateManager, runtimeAdapter: adapter, eventEmitter: storeEmitter, artifactStore: stateManager.piArtifactStore, validator: new DefaultArtificerValidator(), contentHashFn },
|
|
420
|
+
// PRI-741: thread the host semantic projection into generation.
|
|
421
|
+
{ ...runnerOptions, ...(artificerHostSemanticContext !== undefined ? { hostSemanticContext: artificerHostSemanticContext } : {}) });
|
|
373
422
|
break;
|
|
374
423
|
case 'evaluator':
|
|
375
424
|
// P0-D 生产接线: PRI-509 repair loop 正式进入 consumer (bounded,
|
|
@@ -401,6 +450,9 @@ export async function runInternalizationConsumerCycle(workspaceDir, ports) {
|
|
|
401
450
|
...(hostToolCatalog
|
|
402
451
|
? { hostToolCatalog: { readOnlyTools: [...hostToolCatalog.readOnlyTools], writeTools: [...hostToolCatalog.writeTools] } }
|
|
403
452
|
: {}),
|
|
453
|
+
// PRI-741: host-name parity replay case from the SAME registry
|
|
454
|
+
// provenance as the gateDeps above.
|
|
455
|
+
...(artificerHostSemanticContext !== undefined ? { hostSemanticContext: artificerHostSemanticContext } : {}),
|
|
404
456
|
});
|
|
405
457
|
break;
|
|
406
458
|
case 'rollout_reviewer':
|
|
@@ -423,10 +475,18 @@ export async function runInternalizationConsumerCycle(workspaceDir, ports) {
|
|
|
423
475
|
logger.warn(`[PD:${logLabel}] No consumer runner for task kind '${taskKind}'; skipping. Advance manually: pd runtime internalization run-once --runner ${taskKind}`);
|
|
424
476
|
return { ran: false, skipReason: 'no_runner_for_kind', taskKind };
|
|
425
477
|
}
|
|
426
|
-
|
|
478
|
+
// PRI-719: run evidence answers "which declared profile/provider/model
|
|
479
|
+
// actually executed this task" (declared == executed contract).
|
|
480
|
+
const runtimeEvidence = {
|
|
481
|
+
...(taskRuntimeConfig.runtimeProfileId !== undefined ? { runtimeProfileId: taskRuntimeConfig.runtimeProfileId } : {}),
|
|
482
|
+
...(taskRuntimeConfig.provider !== undefined ? { provider: taskRuntimeConfig.provider } : {}),
|
|
483
|
+
...(taskRuntimeConfig.model !== undefined ? { model: taskRuntimeConfig.model } : {}),
|
|
484
|
+
};
|
|
485
|
+
logger.info(`[PD:${logLabel}] Running ${taskKind} task: ${taskId}${Object.keys(runtimeEvidence).length > 0 ? ` (runtime: ${JSON.stringify(runtimeEvidence)})` : ''}`);
|
|
427
486
|
emitEvent('INTERNALIZATION_CONSUMER_RUN', JSON.stringify({
|
|
428
487
|
taskId,
|
|
429
488
|
taskKind,
|
|
489
|
+
...runtimeEvidence,
|
|
430
490
|
}));
|
|
431
491
|
let runResult;
|
|
432
492
|
try {
|