create-principles-disciple 1.145.17 → 1.145.19

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.
Files changed (79) hide show
  1. package/README.md +15 -0
  2. package/_release/product-identity.json +1 -1
  3. package/codex-adapter/package.json +1 -1
  4. package/console/dist/server/models/ActivationsConsoleModel.js +20 -2
  5. package/console/dist/server/models/ApprovalsConsoleModel.js +28 -4
  6. package/console/dist/server/models/ApprovalsGroupedConsoleModel.js +29 -3
  7. package/console/dist/shared/prompt-injection-contract.d.ts +1 -1
  8. package/console/dist/ui/i18n/en.json +4 -1
  9. package/console/dist/ui/i18n/zh-CN.json +4 -1
  10. package/console/dist/ui/pages/activation/ActivationPage.js +3 -1
  11. package/console/dist/ui/pages/control-center/ControlCenterPage.js +11 -1
  12. package/console/dist/ui/pages/focus/FocusPage.js +19 -3
  13. package/console/dist/ui/utils/validators.js +1 -1
  14. package/console/dist/web/assets/app.js +16 -16
  15. package/core/dist/runtime-v2/activation/prompt-budget-policy.d.ts +14 -2
  16. package/core/dist/runtime-v2/activation/prompt-budget-policy.d.ts.map +1 -1
  17. package/core/dist/runtime-v2/activation/prompt-budget-policy.js +24 -2
  18. package/core/dist/runtime-v2/activation/prompt-budget-policy.js.map +1 -1
  19. package/core/dist/runtime-v2/config/index.d.ts +3 -3
  20. package/core/dist/runtime-v2/config/index.d.ts.map +1 -1
  21. package/core/dist/runtime-v2/config/index.js +3 -1
  22. package/core/dist/runtime-v2/config/index.js.map +1 -1
  23. package/core/dist/runtime-v2/config/pd-config-effective.d.ts +8 -1
  24. package/core/dist/runtime-v2/config/pd-config-effective.d.ts.map +1 -1
  25. package/core/dist/runtime-v2/config/pd-config-effective.js +10 -0
  26. package/core/dist/runtime-v2/config/pd-config-effective.js.map +1 -1
  27. package/core/dist/runtime-v2/config/pd-config-redaction.d.ts.map +1 -1
  28. package/core/dist/runtime-v2/config/pd-config-redaction.js +2 -0
  29. package/core/dist/runtime-v2/config/pd-config-redaction.js.map +1 -1
  30. package/core/dist/runtime-v2/config/pd-config-types.d.ts +39 -0
  31. package/core/dist/runtime-v2/config/pd-config-types.d.ts.map +1 -1
  32. package/core/dist/runtime-v2/config/pd-config-types.js +7 -0
  33. package/core/dist/runtime-v2/config/pd-config-types.js.map +1 -1
  34. package/core/dist/runtime-v2/config/pd-config-validate.d.ts.map +1 -1
  35. package/core/dist/runtime-v2/config/pd-config-validate.js +67 -1
  36. package/core/dist/runtime-v2/config/pd-config-validate.js.map +1 -1
  37. package/core/dist/runtime-v2/index.d.ts +2 -2
  38. package/core/dist/runtime-v2/index.d.ts.map +1 -1
  39. package/core/dist/runtime-v2/index.js +3 -1
  40. package/core/dist/runtime-v2/index.js.map +1 -1
  41. package/core/dist/runtime-v2/types/event-types.d.ts +3 -3
  42. package/core/dist/runtime-v2/types/event-types.d.ts.map +1 -1
  43. package/core/dist/runtime-v2/types/event-types.js +1 -0
  44. package/core/dist/runtime-v2/types/event-types.js.map +1 -1
  45. package/core/package.json +1 -1
  46. package/dist/index.d.ts.map +1 -1
  47. package/dist/index.js +5 -1
  48. package/dist/index.js.map +1 -1
  49. package/dist/installers/openclaw-host-installer.d.ts +4 -8
  50. package/dist/installers/openclaw-host-installer.d.ts.map +1 -1
  51. package/dist/installers/openclaw-host-installer.js +10 -84
  52. package/dist/installers/openclaw-host-installer.js.map +1 -1
  53. package/host-runtime/dist/index.js +17 -1
  54. package/host-runtime/dist/prompt-injection-budget-observation.d.ts +10 -1
  55. package/host-runtime/dist/prompt-injection-budget-observation.js +21 -2
  56. package/host-runtime/package.json +1 -1
  57. package/package.json +2 -2
  58. package/pd-cli/dist/commands/config-doctor.d.ts.map +1 -1
  59. package/pd-cli/dist/commands/config-doctor.js +18 -0
  60. package/pd-cli/dist/commands/config-doctor.js.map +1 -1
  61. package/pd-cli/dist/services/config-doctor.d.ts +9 -0
  62. package/pd-cli/dist/services/config-doctor.d.ts.map +1 -1
  63. package/pd-cli/dist/services/config-doctor.js +6 -0
  64. package/pd-cli/dist/services/config-doctor.js.map +1 -1
  65. package/pd-cli/package.json +1 -1
  66. package/plugin/dist/bundle.js +417 -417
  67. package/plugin/dist/governance-audit.js +1 -1
  68. package/plugin/dist/openclaw.plugin.json +1 -1
  69. package/plugin/openclaw.plugin.json +1 -1
  70. package/plugin/package.json +1 -1
  71. package/release-manager/dist/index.d.ts.map +1 -1
  72. package/release-manager/dist/index.js +5 -1
  73. package/release-manager/dist/index.js.map +1 -1
  74. package/release-manager/dist/installers/openclaw-host-installer.d.ts +4 -8
  75. package/release-manager/dist/installers/openclaw-host-installer.d.ts.map +1 -1
  76. package/release-manager/dist/installers/openclaw-host-installer.js +10 -84
  77. package/release-manager/dist/installers/openclaw-host-installer.js.map +1 -1
  78. package/release-manager/package.json +2 -2
  79. package/workbuddy-adapter/package.json +1 -1
package/README.md CHANGED
@@ -46,8 +46,23 @@ npx create-principles-disciple --yes --workspace "/path/to/workspace" --json
46
46
  --non-interactive Skip prompts
47
47
  --workspace <path> Workspace directory
48
48
  --json Output result as JSON (implies non-interactive)
49
+ --provider <name> LLM provider for the pd.default runtime profile (openai/anthropic/deepseek)
50
+ --api-key-env <name> NAME of the env var holding your LLM API key. Requires --provider
51
+ --model <id> LLM model id (optional; the per-provider default applies when omitted)
52
+ --base-url <url> OpenAI-compatible custom endpoint (optional; env: PD_BASE_URL)
49
53
  ```
50
54
 
55
+ ## LLM runtime profile — required before the internal agents can run
56
+
57
+ PD's internal agents (diagnostician, dreamer, scribe, artificer, signal collector) call the LLM
58
+ provider **you** configure. PD ships no keys, stores no key values, and `--api-key-env` names an
59
+ environment variable rather than carrying a secret.
60
+
61
+ `--yes` without `--provider` installs in evidence-only mode: pain signals are captured and kept,
62
+ but diagnosis does not run and no principle is proposed. The post-install summary says so, and at any
63
+ later time `pd config doctor --workspace "<path>"` names the missing field. Full operator walkthrough:
64
+ [`docs/runbooks/USER_GUIDE.md`](../../docs/runbooks/USER_GUIDE.md) — “Before You Start: Give PD an LLM”.
65
+
51
66
  ## Post-Install Verification
52
67
 
53
68
  The installer automatically verifies the installation during setup:
@@ -1,5 +1,5 @@
1
1
  {
2
2
  "schemaVersion": 1,
3
3
  "productVersion": "2.3.10",
4
- "sourceCommit": "72df0b91189c8ad18dbbfefb48c140609bad5c14"
4
+ "sourceCommit": "2eeb69dfb7bdfe0c72a291f4172809b64e70a855"
5
5
  }
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@principles/codex-adapter",
3
- "version": "0.5.10",
3
+ "version": "0.5.12",
4
4
  "description": "Codex CLI host adapter for Principles Disciple — implements HostAdapter interface for OpenAI Codex CLI's stdin/stdout JSON hook model (ADR-0020).",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -1,5 +1,6 @@
1
1
  import { SqliteConnection, SqliteActivationStateStore, SqlitePIArtifactStore, extractPrincipleId, extractEvidenceRefs, PromotionReadinessReader, RuleHostWriter, createProductionGateDeps, SqliteActivationSafetyStore, isFeatureEnabled, RuleCodeOwnerDecisionService, collectOpenClawPromotionChecks, summarizeRuleCodeShadowEvents, buildPromotionEvidenceSnapshot, } from '@principles/core/runtime-v2';
2
- import { resolvePromotionHostLiveness, resolveWorkspaceHostToolSemantics, readPromptActivationInjectionStatuses, runtimeBudgetObservationToResolution, selectConsoleRuntimeBudgetFacts, RUNTIME_BUDGET_OBSERVATION_STALE_REASON, RUNTIME_V2_EVENT_LOG_CANDIDATE_DIRS } from '@principles/host-runtime';
2
+ import { resolvePromotionHostLiveness, resolveWorkspaceHostToolSemantics, readPromptActivationInjectionStatuses, operatorDeclaredBudgetResolution, runtimeBudgetObservationToResolution, selectConsoleRuntimeBudgetFacts, RUNTIME_BUDGET_OBSERVATION_STALE_REASON, RUNTIME_V2_EVENT_LOG_CANDIDATE_DIRS } from '@principles/host-runtime';
3
+ import { PROMPT_BUDGET_HOST_KINDS } from '@principles/core/runtime-v2';
3
4
  import { loadPdConfig, computeFlagsFromLoadResult } from '../config/pd-config-store.js';
4
5
  import * as fs from 'node:fs';
5
6
  import * as path from 'node:path';
@@ -332,6 +333,21 @@ export class ActivationsConsoleModel {
332
333
  ...(targetHost !== undefined ? { targetHost } : {}),
333
334
  });
334
335
  const perHostBudget = new Map([...budgetFacts.perHost].map(([hostKind, observation]) => [hostKind, runtimeBudgetObservationToResolution(observation)]));
336
+ // PRI-1020: hosts with no fresh observation fall back to the Owner's
337
+ // declared basis (.pd/config.yaml promptBudget), same as the grouped
338
+ // surface — observation wins when present.
339
+ const configLoad = loadPdConfig(this.workspaceDir);
340
+ const declaredConfig = configLoad.ok ? configLoad.effective.config.promptBudget : undefined;
341
+ const declaredHosts = new Set();
342
+ for (const hostKind of PROMPT_BUDGET_HOST_KINDS) {
343
+ if (perHostBudget.has(hostKind))
344
+ continue;
345
+ const declared = operatorDeclaredBudgetResolution(declaredConfig, hostKind);
346
+ if (declared !== undefined) {
347
+ perHostBudget.set(hostKind, declared);
348
+ declaredHosts.add(hostKind);
349
+ }
350
+ }
335
351
  const report = await readPromptActivationInjectionStatuses({
336
352
  workspaceDir: this.workspaceDir,
337
353
  ...(targetHost !== undefined ? { targetHost } : {}),
@@ -342,9 +358,11 @@ export class ActivationsConsoleModel {
342
358
  });
343
359
  // The status report resolved the route internally; bind the observation
344
360
  // to THAT host only. A stale observation is never presented as current.
361
+ // PRI-1020: nor when a declared basis actually backed the route — the
362
+ // stale marker would contradict the declared source label.
345
363
  const decidedHost = report.route.status === 'confirmed' ? report.route.hostKind : undefined;
346
364
  const usedObservation = decidedHost !== undefined ? budgetFacts.perHost.get(decidedHost) : undefined;
347
- const staleObservation = decidedHost !== undefined && usedObservation === undefined
365
+ const staleObservation = decidedHost !== undefined && usedObservation === undefined && !declaredHosts.has(decidedHost)
348
366
  ? budgetFacts.stalePerHost.get(decidedHost)
349
367
  : undefined;
350
368
  promptInjectionRoute = {
@@ -3,7 +3,8 @@ import * as path from 'node:path';
3
3
  import { SqliteConnection, SqliteApprovalQueueStore, SqliteActivationStateStore, SqlitePIArtifactStore, ActivationDispatcher, PromptWriter, DeferArchiveWriter, RuleHostWriter, createProductionGateDeps, ApprovalCompletionService, ApprovalQueue, mapConfidenceToLabel, isArtifactRevisionOf, detectPromptReplacementTarget, buildOwnerRevisionArtifact, PrincipleTreeLedgerAdapter, MVP_CHANNELS, } from '@principles/core/runtime-v2';
4
4
  import { resolveLedgerPrincipleId } from './principle-id-resolution.js';
5
5
  import { loadPdConfig, computeFlagsFromLoadResult } from '../config/pd-config-store.js';
6
- import { resolveWorkspaceHostToolSemantics, checkPromptArtifactDeliverabilityById, resolveLivePromptInjectionProjection, runtimeBudgetObservationToResolution, selectConsoleRuntimeBudgetFacts, } from '@principles/host-runtime';
6
+ import { resolveWorkspaceHostToolSemantics, checkPromptArtifactDeliverabilityById, operatorDeclaredBudgetResolution, resolveLivePromptInjectionProjection, runtimeBudgetObservationToResolution, selectConsoleRuntimeBudgetFacts, } from '@principles/host-runtime';
7
+ import { PROMPT_BUDGET_HOST_KINDS } from '@principles/core/runtime-v2';
7
8
  const MVP_PROVEN_CHANNELS = new Set(MVP_CHANNELS);
8
9
  const EMPTY_STATS = { pending: 0, approved: 0, rejected: 0, cancelled: 0 };
9
10
  function isMissingTableError(err) {
@@ -21,7 +22,7 @@ function isActivationSuccess(activation) {
21
22
  * without its own basis lets one host's observation look like another host's
22
23
  * confirmation.
23
24
  */
24
- function describeBudgetBasis(hostKind, facts) {
25
+ function describeBudgetBasis(hostKind, facts, declared) {
25
26
  const used = facts.perHost.get(hostKind);
26
27
  if (used !== undefined) {
27
28
  return used.capacityStatus === 'confirmed'
@@ -31,6 +32,11 @@ function describeBudgetBasis(hostKind, facts) {
31
32
  // budget, not a host window — say so.
32
33
  : `budget basis=runtime_observation_fallback (${hostKind}, runtime reason=${used.fallbackReason ?? 'unknown'}, observed at ${used.observedAt})`;
33
34
  }
35
+ // PRI-1020: the Owner's declared window backing this forecast — name it as
36
+ // a declaration, never as a runtime fact.
37
+ if (declared !== undefined) {
38
+ return `budget basis=operator_declared (${hostKind}, context window declared as ${declared.contextWindowTokens ?? 'unknown'} tokens in .pd/config.yaml)`;
39
+ }
34
40
  const stale = facts.stalePerHost.get(hostKind);
35
41
  if (stale !== undefined) {
36
42
  return `budget basis=fixed_fallback (the runtime budget observation for ${hostKind} is stale — older than 24h and NOT used as the current budget)`;
@@ -327,6 +333,11 @@ export class ApprovalsConsoleModel {
327
333
  async checkPromptInjectionBudget(activationId, targetHost) {
328
334
  let resolution;
329
335
  let budgetFacts;
336
+ // PRI-1020: declared bases per host (Owner's promptBudget config), used
337
+ // when a host has no fresh runtime observation. Scoped here so the basis
338
+ // descriptions at the call sites below see the same facts the projection
339
+ // ran under.
340
+ let declaredBudgetByHost = new Map();
330
341
  // PRI-1017: the post-approval forecast runs under the workspace's most
331
342
  // recent runtime budget OBSERVATION for the target host (per-host for
332
343
  // unconfirmed routes), so the warning describes the budget the last real
@@ -339,6 +350,19 @@ export class ApprovalsConsoleModel {
339
350
  ...(targetHost !== undefined ? { targetHost } : {}),
340
351
  });
341
352
  const perHostBudget = new Map([...budgetFacts.perHost].map(([hostKind, observation]) => [hostKind, runtimeBudgetObservationToResolution(observation)]));
353
+ // PRI-1020: no fresh observation ⇒ the Owner's declared basis, same as
354
+ // the grouped/activations surfaces; a fresh observation still wins.
355
+ const configLoad = loadPdConfig(this.workspaceDir);
356
+ const declaredConfig = configLoad.ok ? configLoad.effective.config.promptBudget : undefined;
357
+ for (const hostKind of PROMPT_BUDGET_HOST_KINDS) {
358
+ if (perHostBudget.has(hostKind))
359
+ continue;
360
+ const declared = operatorDeclaredBudgetResolution(declaredConfig, hostKind);
361
+ if (declared !== undefined) {
362
+ perHostBudget.set(hostKind, declared);
363
+ declaredBudgetByHost.set(hostKind, declared);
364
+ }
365
+ }
342
366
  resolution = await resolveLivePromptInjectionProjection({
343
367
  workspaceDir: this.workspaceDir,
344
368
  ...(targetHost !== undefined ? { targetHost } : {}),
@@ -358,7 +382,7 @@ export class ApprovalsConsoleModel {
358
382
  // Each per-host forecast ran under ITS OWN budget basis (observation,
359
383
  // runtime fallback, stale fallback, or no observation) — the headline
360
384
  // numbers must never imply a single host's confirmation covers them all.
361
- return `injection_capacity_unconfirmed: the activation is committed but this workspace's effective injection route cannot be confirmed (${resolution.decision.unconfirmedReason ?? 'unknown reason'}); per-host forecasts: ${resolution.perHost.map((entry) => `${entry.hostKind}=${entry.projection.route} (${entry.projection.usedChars}c/${entry.projection.budget}c${entry.projection.truncated ? ', truncated' : ''}) ${describeBudgetBasis(entry.hostKind, budgetFacts)}`).join('; ')}. nextAction=${resolution.decision.nextAction ?? 'pass an explicit target host (openclaw|codex|workbuddy)'}`;
385
+ return `injection_capacity_unconfirmed: the activation is committed but this workspace's effective injection route cannot be confirmed (${resolution.decision.unconfirmedReason ?? 'unknown reason'}); per-host forecasts: ${resolution.perHost.map((entry) => `${entry.hostKind}=${entry.projection.route} (${entry.projection.usedChars}c/${entry.projection.budget}c${entry.projection.truncated ? ', truncated' : ''}) ${describeBudgetBasis(entry.hostKind, budgetFacts, declaredBudgetByHost.get(entry.hostKind))}`).join('; ')}. nextAction=${resolution.decision.nextAction ?? 'pass an explicit target host (openclaw|codex|workbuddy)'}`;
362
386
  }
363
387
  const { projection } = resolution;
364
388
  if (projection.injectedActivationIds.includes(activationId)) {
@@ -367,7 +391,7 @@ export class ApprovalsConsoleModel {
367
391
  // Never let a stale observation masquerade as the current budget in the
368
392
  // warning: name the basis the forecast actually ran under.
369
393
  const decidedHost = resolution.decision.hostKind;
370
- const budgetBasisNote = decidedHost === undefined ? '' : ` ${describeBudgetBasis(decidedHost, budgetFacts)}`;
394
+ const budgetBasisNote = decidedHost === undefined ? '' : ` ${describeBudgetBasis(decidedHost, budgetFacts, declaredBudgetByHost.get(decidedHost))}`;
371
395
  const projectionDetail = projection.warnings.length > 0 ? ` projection warnings: ${projection.warnings.join(' | ')}` : '';
372
396
  // AC-02/R-A3: oversized is independent of `truncated` — say WHY first.
373
397
  if (projection.oversizedActivationIds.includes(activationId)) {
@@ -1,6 +1,8 @@
1
1
  import { SqliteConnection, SqliteApprovalQueueStore, SqlitePIArtifactStore, ApprovalQueue, RUNTIME_V2_PRINCIPLE_BUDGET, detectPromptReplacementTarget, SqliteActivationStateStore, } from '@principles/core/runtime-v2';
2
- import { checkPromptArtifactDeliverability, resolveLivePromptInjectionProjection, RUNTIME_BUDGET_OBSERVATION_STALE_REASON, runtimeBudgetObservationToResolution, selectConsoleRuntimeBudgetFacts, } from '@principles/host-runtime';
2
+ import { checkPromptArtifactDeliverability, operatorDeclaredBudgetResolution, resolveLivePromptInjectionProjection, RUNTIME_BUDGET_OBSERVATION_STALE_REASON, runtimeBudgetObservationToResolution, selectConsoleRuntimeBudgetFacts, } from '@principles/host-runtime';
3
+ import { PROMPT_BUDGET_HOST_KINDS } from '@principles/core/runtime-v2';
3
4
  import { loadLedger } from '@principles/core/principle-tree-ledger';
5
+ import { loadPdConfig } from '../config/pd-config-store.js';
4
6
  import { createArtifactPrincipleResolutionDeps, resolveArtifactPrincipleId, } from './artifact-principle-resolver.js';
5
7
  import * as fs from 'node:fs';
6
8
  import * as path from 'node:path';
@@ -391,6 +393,23 @@ export class ApprovalsGroupedConsoleModel {
391
393
  ...(targetHost !== undefined ? { targetHost } : {}),
392
394
  });
393
395
  const perHostBudget = new Map([...facts.perHost].map(([hostKind, observation]) => [hostKind, runtimeBudgetObservationToResolution(observation)]));
396
+ // PRI-1020: a host with NO fresh runtime observation falls back to the
397
+ // Owner's declared basis (.pd/config.yaml promptBudget) instead of the
398
+ // fixed 2000 — resolved through the same tier-0 policy branch the real
399
+ // injection uses, so forecast and injection can never disagree. A fresh
400
+ // observation still wins (it is what the runtime actually did last).
401
+ const configLoad = loadPdConfig(this.workspaceDir);
402
+ const declaredConfig = configLoad.ok ? configLoad.effective.config.promptBudget : undefined;
403
+ const declaredHosts = new Set();
404
+ for (const hostKind of PROMPT_BUDGET_HOST_KINDS) {
405
+ if (perHostBudget.has(hostKind))
406
+ continue;
407
+ const declared = operatorDeclaredBudgetResolution(declaredConfig, hostKind);
408
+ if (declared !== undefined) {
409
+ perHostBudget.set(hostKind, declared);
410
+ declaredHosts.add(hostKind);
411
+ }
412
+ }
394
413
  const resolution = await resolveLivePromptInjectionProjection({
395
414
  workspaceDir: this.workspaceDir,
396
415
  ...(targetHost !== undefined ? { targetHost } : {}),
@@ -424,7 +443,13 @@ export class ApprovalsGroupedConsoleModel {
424
443
  nextAction: resolution.decision.nextAction ?? 'pass an explicit target host (openclaw|codex|workbuddy)',
425
444
  perHost: resolution.perHost.map((entry) => {
426
445
  const observation = facts.perHost.get(entry.hostKind);
427
- const stale = observation === undefined ? facts.stalePerHost.get(entry.hostKind) : undefined;
446
+ // PRI-1020: the stale marker explains a FALLBACK that ignored a
447
+ // too-old observation; it must not contradict a declared basis
448
+ // that was actually used.
449
+ const declaredUsed = observation === undefined && declaredHosts.has(entry.hostKind);
450
+ const stale = observation === undefined && !declaredUsed
451
+ ? facts.stalePerHost.get(entry.hostKind)
452
+ : undefined;
428
453
  return {
429
454
  hostKind: entry.hostKind,
430
455
  route: entry.projection.route,
@@ -450,7 +475,8 @@ export class ApprovalsGroupedConsoleModel {
450
475
  // to whichever host happens to appear first.
451
476
  const decidedHost = resolution.decision.hostKind;
452
477
  const usedObservation = decidedHost !== undefined ? facts.perHost.get(decidedHost) : undefined;
453
- const staleObservation = decidedHost !== undefined && usedObservation === undefined
478
+ const declaredHeadlineUsed = decidedHost !== undefined && usedObservation === undefined && declaredHosts.has(decidedHost);
479
+ const staleObservation = decidedHost !== undefined && usedObservation === undefined && !declaredHeadlineUsed
454
480
  ? facts.stalePerHost.get(decidedHost)
455
481
  : undefined;
456
482
  return {
@@ -15,7 +15,7 @@
15
15
  export type PromptInjectionTargetHostName = 'openclaw' | 'codex' | 'workbuddy';
16
16
  export type PromptInjectionRouteName = 'legacy_trim' | 'shared_render';
17
17
  export type PromptInjectionBudgetScopeName = 'selected_lines' | 'full_directive_context';
18
- export type PromptBudgetCapacitySourceWire = 'host_effective_window' | 'pi_catalog_exact' | 'legacy_fallback';
18
+ export type PromptBudgetCapacitySourceWire = 'operator_declared' | 'host_effective_window' | 'pi_catalog_exact' | 'legacy_fallback';
19
19
  export type PromptBudgetCapacityStatusWire = 'confirmed' | 'unknown';
20
20
  /**
21
21
  * Bounded machine-readable fallback reason. Closed set mirroring the core
@@ -102,6 +102,7 @@
102
102
  "promptCapacityListRoute": "list injection (billed on the selected lines)",
103
103
  "promptCapacityPerHostEntry": "{{host}}: {{billing}} — {{used}}/{{budget}} chars{{full}}{{observed}}",
104
104
  "promptCapacityPerHostFull": " (full)",
105
+ "promptCapacityPerHostDeclared": "(budget from the workspace config declaration)",
105
106
  "promptCapacityPerHostObserved": " (budget from this host's most recent runtime observation at {{observedAt}})",
106
107
  "promptCapacityViewAsHost": "View as {{host}}",
107
108
  "promptCapacityClearHost": "Clear selection",
@@ -116,6 +117,8 @@
116
117
  "promptBudgetFallbackObservedSuffix": "; the runtime's most recent observation was at {{observedAt}}",
117
118
  "budgetCapacitySourceHostWindow": "host effective context window",
118
119
  "budgetCapacitySourcePiCatalog": "exact model catalog match",
120
+ "budgetCapacitySourceOperatorDeclared": "workspace config declaration",
121
+ "budgetCapacitySourceOperatorDeclaredSuffix": ", source: workspace config declaration",
119
122
  "budgetFallbackReason": {
120
123
  "host_capacity_absent": "the host provided no context capacity fact",
121
124
  "host_capacity_source_untrusted": "the host capacity source is untrusted",
@@ -1456,7 +1459,7 @@
1456
1459
  "configDisabled": "All agents are disabled.",
1457
1460
  "configErrors": "Configuration Errors",
1458
1461
  "internalAgents": "Internal Agents",
1459
- "internalAgentsDescription": "Each PD internal agent uses an OpenClaw runtime profile. Keys stay outside PD.",
1462
+ "internalAgentsDescription": "Each PD internal agent runs on the runtime profile you configure — provider, model, and the name of the environment variable holding your API key. Keys stay outside PD.",
1460
1463
  "defaultRuntime": "Default Runtime",
1461
1464
  "defaultRuntimeDescription": "The default runtime profile for agents without explicit overrides.",
1462
1465
  "advancedDiagnostics": "Advanced Diagnostics",
@@ -102,6 +102,7 @@
102
102
  "promptCapacityListRoute": "列表注入(按选中行计费)",
103
103
  "promptCapacityPerHostEntry": "{{host}}:{{billing}} — {{used}}/{{budget}} 字符{{full}}{{observed}}",
104
104
  "promptCapacityPerHostFull": "(已满)",
105
+ "promptCapacityPerHostDeclared": "(预算来自工作区配置声明)",
105
106
  "promptCapacityPerHostObserved": "(预算来自该宿主最近一次运行时观测,观测于 {{observedAt}})",
106
107
  "promptCapacityViewAsHost": "按 {{host}} 查看",
107
108
  "promptCapacityClearHost": "清除选择",
@@ -116,6 +117,8 @@
116
117
  "promptBudgetFallbackObservedSuffix": ";运行时最近一次观测于 {{observedAt}}",
117
118
  "budgetCapacitySourceHostWindow": "宿主有效上下文窗口",
118
119
  "budgetCapacitySourcePiCatalog": "模型目录精确匹配",
120
+ "budgetCapacitySourceOperatorDeclared": "工作区配置声明",
121
+ "budgetCapacitySourceOperatorDeclaredSuffix": ",来源: 工作区配置声明",
119
122
  "budgetFallbackReason": {
120
123
  "host_capacity_absent": "宿主未提供上下文容量事实",
121
124
  "host_capacity_source_untrusted": "宿主容量来源不可信",
@@ -1456,7 +1459,7 @@
1456
1459
  "configDisabled": "所有智能体已停用。",
1457
1460
  "configErrors": "配置错误",
1458
1461
  "internalAgents": "内部智能体",
1459
- "internalAgentsDescription": "每个 PD 内部智能体使用一个 OpenClaw 运行时配置。密钥留在 PD 之外。",
1462
+ "internalAgentsDescription": "每个 PD 内部智能体都运行在您配置的运行时配置上——provider、model,以及存放 API Key 的环境变量名。密钥本身留在 PD 之外。",
1460
1463
  "defaultRuntime": "默认运行时",
1461
1464
  "defaultRuntimeDescription": "未设置显式覆盖的智能体使用的默认运行时配置。",
1462
1465
  "advancedDiagnostics": "高级诊断",
@@ -414,7 +414,9 @@ export function ActivationPage() {
414
414
  defaultValue: "注入状态中的预算数字取自最近一次运行时观测(来源 {{source}},上下文 {{window}} tokens,观测于 {{observedAt}}),并非实时确认值。",
415
415
  source: route.budgetCapacitySource === "pi_catalog_exact"
416
416
  ? t("pages.focus.budgetCapacitySourcePiCatalog", { defaultValue: "模型目录精确匹配" })
417
- : t("pages.focus.budgetCapacitySourceHostWindow", { defaultValue: "宿主有效上下文窗口" }),
417
+ : route.budgetCapacitySource === "operator_declared"
418
+ ? t("pages.focus.budgetCapacitySourceOperatorDeclared", { defaultValue: "工作区配置声明" })
419
+ : t("pages.focus.budgetCapacitySourceHostWindow", { defaultValue: "宿主有效上下文窗口" }),
418
420
  window: route.budgetContextWindowTokens,
419
421
  observedAt: route.budgetObservedAt,
420
422
  }) }));
@@ -27,6 +27,14 @@ function validateSource(raw) {
27
27
  function isRecord(value) {
28
28
  return typeof value === "object" && value !== null && !Array.isArray(value);
29
29
  }
30
+ // Overall readiness → the plain-language sentence shown under the internal-agents heading.
31
+ const CONFIG_READINESS_KEY = {
32
+ ready: "pages.controlCenter.configReady",
33
+ needs_setup: "pages.controlCenter.configNeedsSetup",
34
+ not_ready: "pages.controlCenter.configNotReady",
35
+ unknown: "pages.controlCenter.configUnknown",
36
+ disabled: "pages.controlCenter.configDisabled",
37
+ };
30
38
  function validateReadinessStatus(raw) {
31
39
  if (typeof raw !== "string")
32
40
  return null;
@@ -486,7 +494,9 @@ export function ControlCenterPage() {
486
494
  };
487
495
  const overallReadiness = computeOverallReadiness(diagnostics);
488
496
  const availableProfiles = catalogData?.profiles ?? configData.runtimeProfiles;
489
- return (_jsx(PageShell, { children: _jsxs("div", { className: "animate-[pdFadeIn_400ms_ease-out]", children: [_jsx("div", { className: "font-mono text-[12px] tracking-[0.14em] text-ink-3 uppercase mb-3", children: t("pages.controlCenter.eyebrow") }), _jsx("h1", { className: "text-[29px] font-semibold tracking-tight text-ink mb-2", children: t("pages.controlCenter.title") }), _jsx("p", { className: "text-ink-3 text-[14px] max-w-[760px] leading-relaxed mb-7", children: t("pages.controlCenter.subtitle") }), _jsx(WorkflowDiagram, {}), _jsx(CompactStatusBar, { readiness: overallReadiness, agents: configData.agents }), _jsx(DesignGatePanel, {}), _jsxs("section", { className: "mt-8", "aria-labelledby": "section-agents", children: [_jsx(SectionTitle, { id: "section-agents", children: t("pages.controlCenter.internalAgents") }), _jsx("p", { className: "text-ink-3 text-[13px] leading-relaxed mb-3", children: t("pages.controlCenter.internalAgentsDescription") }), (() => {
497
+ return (_jsx(PageShell, { children: _jsxs("div", { className: "animate-[pdFadeIn_400ms_ease-out]", children: [_jsx("div", { className: "font-mono text-[12px] tracking-[0.14em] text-ink-3 uppercase mb-3", children: t("pages.controlCenter.eyebrow") }), _jsx("h1", { className: "text-[29px] font-semibold tracking-tight text-ink mb-2", children: t("pages.controlCenter.title") }), _jsx("p", { className: "text-ink-3 text-[14px] max-w-[760px] leading-relaxed mb-7", children: t("pages.controlCenter.subtitle") }), _jsx(WorkflowDiagram, {}), _jsx(CompactStatusBar, { readiness: overallReadiness, agents: configData.agents }), _jsx(DesignGatePanel, {}), _jsxs("section", { className: "mt-8", "aria-labelledby": "section-agents", children: [_jsx(SectionTitle, { id: "section-agents", children: t("pages.controlCenter.internalAgents") }), _jsx("p", { className: "text-ink-3 text-[13px] leading-relaxed mb-3", children: t("pages.controlCenter.internalAgentsDescription") }), _jsx("p", { className: overallReadiness === "ready"
498
+ ? "text-ink-4 text-[13px] leading-relaxed mb-3"
499
+ : "text-amber text-[13px] leading-relaxed mb-3", children: t(CONFIG_READINESS_KEY[overallReadiness]) }), (() => {
490
500
  const { groups, unknown } = groupAgentsByDependency(configData.agents);
491
501
  const locale = i18n.language.startsWith("en")
492
502
  ? "en"
@@ -441,15 +441,24 @@ function PendingReviewCard({ group, actionsLockedReason, onDecisionApplied, prom
441
441
  // present a past observation as a live confirmation.
442
442
  ? t("pages.focus.promptBudgetObservedNote", {
443
443
  defaultValue: "预算取自最近一次运行时观测(来源 {{source}},上下文 {{window}} tokens,观测于 {{observedAt}})",
444
+ // PRI-1020: three-way source label — the owner-declared
445
+ // basis reads as a declaration, never as a runtime fact.
444
446
  source: promptInjection.budgetCapacitySource === "pi_catalog_exact"
445
447
  ? t("pages.focus.budgetCapacitySourcePiCatalog", { defaultValue: "模型目录精确匹配" })
446
- : t("pages.focus.budgetCapacitySourceHostWindow", { defaultValue: "宿主有效上下文窗口" }),
448
+ : promptInjection.budgetCapacitySource === "operator_declared"
449
+ ? t("pages.focus.budgetCapacitySourceOperatorDeclared", { defaultValue: "工作区配置声明" })
450
+ : t("pages.focus.budgetCapacitySourceHostWindow", { defaultValue: "宿主有效上下文窗口" }),
447
451
  window: promptInjection.budgetContextWindowTokens,
448
452
  observedAt: promptInjection.budgetObservedAt,
449
453
  })
450
454
  : t("pages.focus.promptBudgetModelAwareNote", {
451
- defaultValue: "预算按已确认的模型上下文解析({{window}} tokens)",
455
+ defaultValue: "预算按已确认的模型上下文解析({{window}} tokens{{sourceSuffix}})",
452
456
  window: promptInjection.budgetContextWindowTokens,
457
+ // PRI-1020: without an observation, a confirmed budget can
458
+ // only come from the Owner's declaration — say so.
459
+ sourceSuffix: promptInjection.budgetCapacitySource === "operator_declared"
460
+ ? t("pages.focus.budgetCapacitySourceOperatorDeclaredSuffix", { defaultValue: ",来源: 工作区配置声明" })
461
+ : "",
453
462
  })
454
463
  : promptInjection.budgetObservedAt !== undefined
455
464
  // PRI-1017: a fallback that still carries an observation
@@ -822,7 +831,14 @@ export function FocusPage({ featureFlags }) {
822
831
  defaultValue: "(预算来自该宿主最近一次运行时观测,观测于 {{observedAt}})",
823
832
  observedAt: entry.budgetObservedAt,
824
833
  })
825
- : "",
834
+ // PRI-1020: a declared basis is the Owner's own config
835
+ // fact — label it as a declaration, never as an
836
+ // observation.
837
+ : entry.budgetCapacitySource === "operator_declared"
838
+ ? t("pages.focus.promptCapacityPerHostDeclared", {
839
+ defaultValue: "(预算来自工作区配置声明)",
840
+ })
841
+ : "",
826
842
  }), entry.budgetFallbackReason !== undefined && (_jsxs("span", { "data-testid": `prompt-capacity-perhost-reason-${entry.hostKind}`, children: [" · ", t(`pages.focus.budgetFallbackReason.${entry.budgetFallbackReason}`, {
827
843
  defaultValue: entry.budgetFallbackReason,
828
844
  })] }))] }, entry.hostKind))) }), unconfirmed.nextAction !== undefined && (_jsx("p", { className: "mt-1 text-ink-2", children: unconfirmed.nextAction })), _jsxs("div", { className: "mt-2 flex gap-2", children: [["openclaw", "codex", "workbuddy"].map((host) => (_jsx("button", { type: "button", onClick: () => setPromptTargetHost(host), "data-testid": `prompt-capacity-pick-${host}`, className: `inline-flex items-center border rounded-[3px] px-[10px] py-[4px] transition-colors ${promptTargetHost === host ? "border-amber bg-amber/10" : "border-line hover:border-line-2"}`, children: t("pages.focus.promptCapacityViewAsHost", { host: host === "openclaw" ? "OpenClaw" : host === "workbuddy" ? "WorkBuddy" : "Codex" }) }, host))), promptTargetHost !== null && (_jsx("button", { type: "button", onClick: () => setPromptTargetHost(null), "data-testid": "prompt-capacity-pick-clear", className: "inline-flex items-center border border-line rounded-[3px] px-[10px] py-[4px] hover:border-line-2", children: t("pages.focus.promptCapacityClearHost", { defaultValue: "清除选择" }) }))] })] }));
@@ -85,7 +85,7 @@ function isPromptBudgetFallbackReason(value) {
85
85
  return isString(value) && PROMPT_BUDGET_FALLBACK_REASONS.has(value);
86
86
  }
87
87
  function isPromptBudgetCapacitySource(value) {
88
- return value === 'host_effective_window' || value === 'pi_catalog_exact' || value === 'legacy_fallback';
88
+ return value === 'operator_declared' || value === 'host_effective_window' || value === 'pi_catalog_exact' || value === 'legacy_fallback';
89
89
  }
90
90
  function isPromptBudgetCapacityStatus(value) {
91
91
  return value === 'confirmed' || value === 'unknown';