@principles/host-runtime 0.3.11 → 0.3.12

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.
@@ -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, 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';
@@ -240,13 +240,50 @@ export async function runInternalizationConsumerCycle(workspaceDir, ports) {
240
240
  // Advance the configured runner kinds in priority order (dreamer first,
241
241
  // then philosopher→…→rollout_reviewer under full-chain scope). Lease the
242
242
  // first ready task whose dependencies are satisfied.
243
+ //
244
+ // PRI-719 (EP002-R2 F4): the runtime profile is resolved PER KIND, before
245
+ // wakeOnce — `internalAgents.agents[kind].runtimeProfile` (fallback
246
+ // defaultRuntime) now actually governs the stage that runs on it. Before,
247
+ // the whole chain shared the diagnostician binding and every per-agent
248
+ // profile declaration was silently ignored. A kind whose profile cannot
249
+ // resolve is skipped observably (no lease taken for it).
243
250
  let wakeResult = null;
244
251
  let lastSkipDecision = 'no_ready_tasks';
245
252
  let lastSkipReason;
253
+ let taskRuntimeConfig = null;
246
254
  for (const kind of decision.runnerKinds) {
255
+ const agentName = AGENT_NAME_FOR_TASK_KIND[kind];
256
+ if (agentName === undefined) {
257
+ lastSkipDecision = 'runtime_config_error';
258
+ lastSkipReason = `no internal agent mapping for task kind '${kind}'`;
259
+ continue;
260
+ }
261
+ const kindRuntime = resolveRuntimeConfigForAgent(configResult.effective, agentName, {
262
+ getEnvVar: (name) => envGetter(name),
263
+ // Consumer stage scope is the internalization_full_chain flag, not
264
+ // agents[kind].enabled (the shipped default disables
265
+ // philosopher/evaluator/rolloutReviewer yet full-chain runs them) —
266
+ // see ResolveRuntimeConfigForAgentOptions.
267
+ ignoreAgentEnabled: true,
268
+ });
269
+ if (isRuntimeConfigError(kindRuntime)) {
270
+ // rc-9: the degradation is observable per kind; other kinds still run.
271
+ emitEvent('INTERNALIZATION_CONSUMER_SKIP', JSON.stringify({
272
+ reason: 'runtime_config_error',
273
+ taskKind: kind,
274
+ agentName,
275
+ message: kindRuntime.message,
276
+ nextAction: kindRuntime.nextAction,
277
+ }));
278
+ logger.warn(`[PD:${logLabel}] Runtime config error for ${kind} (agent ${agentName}): ${kindRuntime.message}`);
279
+ lastSkipDecision = 'runtime_config_error';
280
+ lastSkipReason = kindRuntime.message;
281
+ continue;
282
+ }
247
283
  const candidate = await orchestrator.wakeOnce(kind);
248
284
  if (candidate.decision === 'would_lease') {
249
285
  wakeResult = candidate;
286
+ taskRuntimeConfig = kindRuntime;
250
287
  break;
251
288
  }
252
289
  // Keep decision/reason paired across iterations so the final SKIP log
@@ -254,7 +291,7 @@ export async function runInternalizationConsumerCycle(workspaceDir, ports) {
254
291
  lastSkipDecision = candidate.decision;
255
292
  lastSkipReason = candidate.decision === 'no_ready_tasks' ? candidate.reason : undefined;
256
293
  }
257
- if (!wakeResult) {
294
+ if (!wakeResult || taskRuntimeConfig === null) {
258
295
  // A: 预算由周期 finally 统一执行 (每周期恰一次,含 backlog 场景)
259
296
  const skipPayload = { decision: lastSkipDecision };
260
297
  if (lastSkipReason) {
@@ -264,8 +301,11 @@ export async function runInternalizationConsumerCycle(workspaceDir, ports) {
264
301
  logger.info(`[PD:${logLabel}] No task to consume: ${lastSkipDecision}`);
265
302
  return { ran: false, skipReason: lastSkipDecision };
266
303
  }
304
+ // PRI-719: everything downstream reads the LEASED TASK's resolved config —
305
+ // the kind-specific profile, not the shared gate resolution.
306
+ const taskRuntimeKind = taskRuntimeConfig.runtimeKind;
267
307
  let adapter;
268
- if (runtimeKind === 'pi-ai') {
308
+ if (taskRuntimeKind === 'pi-ai') {
269
309
  // PRI-419: when l2_dreamer flag is on AND this is a dreamer task, route
270
310
  // through the L2 multi-turn agent loop. Non-dreamer runners always use PiAi.
271
311
  const l2Flag = loadFeatureFlagFromConfig(workspaceDir, 'l2_dreamer');
@@ -275,14 +315,14 @@ export async function runInternalizationConsumerCycle(workspaceDir, ports) {
275
315
  logger: { warn: (msg) => logger.warn(msg) },
276
316
  });
277
317
  adapter = new L2AgentLoopAdapter({
278
- provider: runtimeConfigResult.provider ?? 'openai',
279
- model: runtimeConfigResult.model ?? 'gpt-4o',
280
- apiKeyEnv: runtimeConfigResult.apiKeyEnv ?? 'OPENAI_API_KEY',
281
- baseUrl: runtimeConfigResult.baseUrl,
318
+ provider: taskRuntimeConfig.provider ?? 'openai',
319
+ model: taskRuntimeConfig.model ?? 'gpt-4o',
320
+ apiKeyEnv: taskRuntimeConfig.apiKeyEnv ?? 'OPENAI_API_KEY',
321
+ baseUrl: taskRuntimeConfig.baseUrl,
282
322
  workspace: workspaceDir,
283
- totalBudgetMs: runtimeConfigResult.timeoutMs,
323
+ totalBudgetMs: taskRuntimeConfig.timeoutMs,
284
324
  // PRI-633: profile systemPrompt rides as the append layer.
285
- ...(runtimeConfigResult.systemPrompt ? { systemPrompt: runtimeConfigResult.systemPrompt } : {}),
325
+ ...(taskRuntimeConfig.systemPrompt ? { systemPrompt: taskRuntimeConfig.systemPrompt } : {}),
286
326
  }, {
287
327
  artifactReader: {
288
328
  // Explicit adapter: PIArtifactRecord → PdL2ArtifactReader. The store returns
@@ -301,27 +341,27 @@ export async function runInternalizationConsumerCycle(workspaceDir, ports) {
301
341
  }
302
342
  else {
303
343
  adapter = new PiAiRuntimeAdapter({
304
- provider: runtimeConfigResult.provider ?? 'openai',
305
- model: runtimeConfigResult.model ?? 'gpt-4o',
306
- apiKeyEnv: runtimeConfigResult.apiKeyEnv ?? 'OPENAI_API_KEY',
307
- maxRetries: runtimeConfigResult.maxRetries,
308
- maxTokens: runtimeConfigResult.maxTokens,
309
- timeoutMs: runtimeConfigResult.timeoutMs,
310
- baseUrl: runtimeConfigResult.baseUrl,
344
+ provider: taskRuntimeConfig.provider ?? 'openai',
345
+ model: taskRuntimeConfig.model ?? 'gpt-4o',
346
+ apiKeyEnv: taskRuntimeConfig.apiKeyEnv ?? 'OPENAI_API_KEY',
347
+ maxRetries: taskRuntimeConfig.maxRetries,
348
+ maxTokens: taskRuntimeConfig.maxTokens,
349
+ timeoutMs: taskRuntimeConfig.timeoutMs,
350
+ baseUrl: taskRuntimeConfig.baseUrl,
311
351
  workspace: workspaceDir,
312
352
  // PRI-633: profile systemPrompt rides as the append layer.
313
- ...(runtimeConfigResult.systemPrompt ? { systemPrompt: runtimeConfigResult.systemPrompt } : {}),
353
+ ...(taskRuntimeConfig.systemPrompt ? { systemPrompt: taskRuntimeConfig.systemPrompt } : {}),
314
354
  });
315
355
  }
316
356
  }
317
- else if (runtimeKind === 'openclaw-cli') {
357
+ else if (taskRuntimeKind === 'openclaw-cli') {
318
358
  adapter = new OpenClawCliRuntimeAdapter({
319
- runtimeMode: runtimeConfigResult.openclawMode ?? 'default',
359
+ runtimeMode: taskRuntimeConfig.openclawMode ?? 'default',
320
360
  workspaceDir: workspaceDir,
321
361
  });
322
362
  }
323
363
  else {
324
- throw new Error(`Unsupported runtime kind resolved for auto-consumer: ${runtimeKind}`);
364
+ throw new Error(`Unsupported runtime kind resolved for auto-consumer: ${taskRuntimeKind}`);
325
365
  }
326
366
  const { taskId } = wakeResult;
327
367
  const { taskKind } = wakeResult;
@@ -340,9 +380,9 @@ export async function runInternalizationConsumerCycle(workspaceDir, ports) {
340
380
  // same effective config (EP-07 canonical value).
341
381
  const runnerOptions = {
342
382
  owner,
343
- runtimeKind,
383
+ runtimeKind: taskRuntimeKind,
344
384
  effectiveConfig: configResult.effective,
345
- timeoutMs: runtimeConfigResult.timeoutMs,
385
+ timeoutMs: taskRuntimeConfig.timeoutMs,
346
386
  outputLanguage: resolveOutputLanguage(configResult.effective.config.principles?.outputLanguage).outputLanguage,
347
387
  };
348
388
  // PRI-634 A3: workspace-scoped telemetry sink for the evaluator runner.
@@ -423,10 +463,18 @@ export async function runInternalizationConsumerCycle(workspaceDir, ports) {
423
463
  logger.warn(`[PD:${logLabel}] No consumer runner for task kind '${taskKind}'; skipping. Advance manually: pd runtime internalization run-once --runner ${taskKind}`);
424
464
  return { ran: false, skipReason: 'no_runner_for_kind', taskKind };
425
465
  }
426
- logger.info(`[PD:${logLabel}] Running ${taskKind} task: ${taskId}`);
466
+ // PRI-719: run evidence answers "which declared profile/provider/model
467
+ // actually executed this task" (declared == executed contract).
468
+ const runtimeEvidence = {
469
+ ...(taskRuntimeConfig.runtimeProfileId !== undefined ? { runtimeProfileId: taskRuntimeConfig.runtimeProfileId } : {}),
470
+ ...(taskRuntimeConfig.provider !== undefined ? { provider: taskRuntimeConfig.provider } : {}),
471
+ ...(taskRuntimeConfig.model !== undefined ? { model: taskRuntimeConfig.model } : {}),
472
+ };
473
+ logger.info(`[PD:${logLabel}] Running ${taskKind} task: ${taskId}${Object.keys(runtimeEvidence).length > 0 ? ` (runtime: ${JSON.stringify(runtimeEvidence)})` : ''}`);
427
474
  emitEvent('INTERNALIZATION_CONSUMER_RUN', JSON.stringify({
428
475
  taskId,
429
476
  taskKind,
477
+ ...runtimeEvidence,
430
478
  }));
431
479
  let runResult;
432
480
  try {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@principles/host-runtime",
3
- "version": "0.3.11",
3
+ "version": "0.3.12",
4
4
  "description": "Shared host-neutral orchestration for Principles Disciple MVP-Core hook paths.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",