create-principles-disciple 1.145.16 → 1.145.18

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 (112) hide show
  1. package/_release/product-identity.json +2 -2
  2. package/codex-adapter/dist/worker/workspace-worker.js +2 -1
  3. package/codex-adapter/package.json +1 -1
  4. package/console/dist/server/index.js +12 -0
  5. package/console/dist/server/models/ActivationsConsoleModel.js +20 -2
  6. package/console/dist/server/models/ApprovalsConsoleModel.js +28 -4
  7. package/console/dist/server/models/ApprovalsGroupedConsoleModel.js +29 -3
  8. package/console/dist/server/routes/design-gate.d.ts +11 -0
  9. package/console/dist/server/routes/design-gate.js +156 -0
  10. package/console/dist/shared/prompt-injection-contract.d.ts +1 -1
  11. package/console/dist/ui/api.d.ts +29 -1
  12. package/console/dist/ui/api.js +105 -1
  13. package/console/dist/ui/i18n/en.json +48 -0
  14. package/console/dist/ui/i18n/zh-CN.json +48 -0
  15. package/console/dist/ui/pages/activation/ActivationPage.js +3 -1
  16. package/console/dist/ui/pages/control-center/ControlCenterPage.js +2 -1
  17. package/console/dist/ui/pages/control-center/DesignGatePanel.d.ts +1 -0
  18. package/console/dist/ui/pages/control-center/DesignGatePanel.js +137 -0
  19. package/console/dist/ui/pages/focus/FocusPage.js +19 -3
  20. package/console/dist/ui/utils/validators.js +1 -1
  21. package/console/dist/web/assets/app.css +6 -0
  22. package/console/dist/web/assets/app.js +30 -30
  23. package/core/dist/runtime-v2/activation/prompt-budget-policy.d.ts +14 -2
  24. package/core/dist/runtime-v2/activation/prompt-budget-policy.d.ts.map +1 -1
  25. package/core/dist/runtime-v2/activation/prompt-budget-policy.js +24 -2
  26. package/core/dist/runtime-v2/activation/prompt-budget-policy.js.map +1 -1
  27. package/core/dist/runtime-v2/config/index.d.ts +3 -3
  28. package/core/dist/runtime-v2/config/index.d.ts.map +1 -1
  29. package/core/dist/runtime-v2/config/index.js +3 -1
  30. package/core/dist/runtime-v2/config/index.js.map +1 -1
  31. package/core/dist/runtime-v2/config/pd-config-effective.d.ts +8 -1
  32. package/core/dist/runtime-v2/config/pd-config-effective.d.ts.map +1 -1
  33. package/core/dist/runtime-v2/config/pd-config-effective.js +10 -0
  34. package/core/dist/runtime-v2/config/pd-config-effective.js.map +1 -1
  35. package/core/dist/runtime-v2/config/pd-config-redaction.d.ts.map +1 -1
  36. package/core/dist/runtime-v2/config/pd-config-redaction.js +2 -0
  37. package/core/dist/runtime-v2/config/pd-config-redaction.js.map +1 -1
  38. package/core/dist/runtime-v2/config/pd-config-types.d.ts +39 -0
  39. package/core/dist/runtime-v2/config/pd-config-types.d.ts.map +1 -1
  40. package/core/dist/runtime-v2/config/pd-config-types.js +7 -0
  41. package/core/dist/runtime-v2/config/pd-config-types.js.map +1 -1
  42. package/core/dist/runtime-v2/config/pd-config-validate.d.ts.map +1 -1
  43. package/core/dist/runtime-v2/config/pd-config-validate.js +67 -1
  44. package/core/dist/runtime-v2/config/pd-config-validate.js.map +1 -1
  45. package/core/dist/runtime-v2/index.d.ts +5 -2
  46. package/core/dist/runtime-v2/index.d.ts.map +1 -1
  47. package/core/dist/runtime-v2/index.js +6 -1
  48. package/core/dist/runtime-v2/index.js.map +1 -1
  49. package/core/dist/runtime-v2/internalization/artificer-intent-preflight.d.ts +12 -0
  50. package/core/dist/runtime-v2/internalization/artificer-intent-preflight.d.ts.map +1 -1
  51. package/core/dist/runtime-v2/internalization/artificer-intent-preflight.js +23 -0
  52. package/core/dist/runtime-v2/internalization/artificer-intent-preflight.js.map +1 -1
  53. package/core/dist/runtime-v2/internalization/artificer-prompt-builder.d.ts +1 -1
  54. package/core/dist/runtime-v2/internalization/artificer-prompt-builder.d.ts.map +1 -1
  55. package/core/dist/runtime-v2/internalization/artificer-prompt-builder.js +15 -5
  56. package/core/dist/runtime-v2/internalization/artificer-prompt-builder.js.map +1 -1
  57. package/core/dist/runtime-v2/internalization/design-gate-facts.d.ts +54 -0
  58. package/core/dist/runtime-v2/internalization/design-gate-facts.d.ts.map +1 -0
  59. package/core/dist/runtime-v2/internalization/design-gate-facts.js +172 -0
  60. package/core/dist/runtime-v2/internalization/design-gate-facts.js.map +1 -0
  61. package/core/dist/runtime-v2/internalization/evaluator-prompt-builder.d.ts +8 -1
  62. package/core/dist/runtime-v2/internalization/evaluator-prompt-builder.d.ts.map +1 -1
  63. package/core/dist/runtime-v2/internalization/evaluator-prompt-builder.js +73 -3
  64. package/core/dist/runtime-v2/internalization/evaluator-prompt-builder.js.map +1 -1
  65. package/core/dist/runtime-v2/internalization/evaluator-runner.d.ts.map +1 -1
  66. package/core/dist/runtime-v2/internalization/evaluator-runner.js +64 -2
  67. package/core/dist/runtime-v2/internalization/evaluator-runner.js.map +1 -1
  68. package/core/dist/runtime-v2/internalization/rule-context-v2.d.ts +3 -0
  69. package/core/dist/runtime-v2/internalization/rule-context-v2.d.ts.map +1 -1
  70. package/core/dist/runtime-v2/internalization/rule-context-v2.js +4 -0
  71. package/core/dist/runtime-v2/internalization/rule-context-v2.js.map +1 -1
  72. package/core/dist/runtime-v2/store/design-gate/sqlite-design-gate-store.d.ts +49 -0
  73. package/core/dist/runtime-v2/store/design-gate/sqlite-design-gate-store.d.ts.map +1 -0
  74. package/core/dist/runtime-v2/store/design-gate/sqlite-design-gate-store.js +274 -0
  75. package/core/dist/runtime-v2/store/design-gate/sqlite-design-gate-store.js.map +1 -0
  76. package/core/dist/runtime-v2/store/sqlite-connection.d.ts.map +1 -1
  77. package/core/dist/runtime-v2/store/sqlite-connection.js +31 -0
  78. package/core/dist/runtime-v2/store/sqlite-connection.js.map +1 -1
  79. package/core/dist/runtime-v2/types/event-types.d.ts +3 -3
  80. package/core/dist/runtime-v2/types/event-types.d.ts.map +1 -1
  81. package/core/dist/runtime-v2/types/event-types.js +1 -0
  82. package/core/dist/runtime-v2/types/event-types.js.map +1 -1
  83. package/core/package.json +5 -1
  84. package/host-runtime/dist/codex-artificer-trusted-facts.d.ts +7 -0
  85. package/host-runtime/dist/codex-artificer-trusted-facts.js +17 -0
  86. package/host-runtime/dist/codex-rule-context-provider.js +82 -13
  87. package/host-runtime/dist/design-gate-facts.d.ts +37 -0
  88. package/host-runtime/dist/design-gate-facts.js +249 -0
  89. package/host-runtime/dist/index.d.ts +3 -0
  90. package/host-runtime/dist/index.js +20 -1
  91. package/host-runtime/dist/production-rulehost-gate.js +37 -1
  92. package/host-runtime/dist/prompt-injection-budget-observation.d.ts +10 -1
  93. package/host-runtime/dist/prompt-injection-budget-observation.js +21 -2
  94. package/host-runtime/dist/workspace-behavior-evidence.d.ts +9 -0
  95. package/host-runtime/dist/workspace-behavior-evidence.js +85 -0
  96. package/host-runtime/package.json +1 -1
  97. package/package.json +2 -2
  98. package/pd-cli/dist/commands/config-doctor.d.ts.map +1 -1
  99. package/pd-cli/dist/commands/config-doctor.js +18 -0
  100. package/pd-cli/dist/commands/config-doctor.js.map +1 -1
  101. package/pd-cli/dist/services/config-doctor.d.ts +9 -0
  102. package/pd-cli/dist/services/config-doctor.d.ts.map +1 -1
  103. package/pd-cli/dist/services/config-doctor.js +6 -0
  104. package/pd-cli/dist/services/config-doctor.js.map +1 -1
  105. package/plugin/dist/bundle.js +462 -432
  106. package/plugin/dist/governance-audit.js +1 -1
  107. package/plugin/dist/openclaw.plugin.json +1 -1
  108. package/plugin/dist/rulehost-evidence.js +24 -24
  109. package/plugin/openclaw.plugin.json +1 -1
  110. package/plugin/package.json +1 -1
  111. package/release-manager/package.json +2 -2
  112. package/workbuddy-adapter/package.json +1 -1
@@ -1,5 +1,5 @@
1
1
  {
2
2
  "schemaVersion": 1,
3
- "productVersion": "2.3.9",
4
- "sourceCommit": "a821a6fe469e297285c206a53f5175a6de679aa7"
3
+ "productVersion": "2.3.10",
4
+ "sourceCommit": "de6c5bd575e8135aab7bf0bc9f2d9c04e4754f7e"
5
5
  }
@@ -28,7 +28,7 @@
28
28
  import fs from 'node:fs';
29
29
  import path from 'node:path';
30
30
  import { computeFeatureFlagsFromConfig, createRuntimeStateHandle, createPainSignalBridge, isRetryWaitBackoffElapsed, PrincipleTreeLedgerAdapter, resolveDiagnosticianCapability, } from '@principles/core/runtime-v2';
31
- import { createWorkspaceBehaviorEvidencePort, loadPdConfigForPlugin, loadFeatureFlagFromConfig, reconcileGovernanceContinuation, runInternalizationConsumerCycle, saveHostToolDeclaration, } from '@principles/host-runtime';
31
+ import { createWorkspaceBehaviorEvidencePort, createCodexArtificerTrustedFactsPort, loadPdConfigForPlugin, loadFeatureFlagFromConfig, reconcileGovernanceContinuation, runInternalizationConsumerCycle, saveHostToolDeclaration, } from '@principles/host-runtime';
32
32
  import { catchUpCodexIngestion } from '../ingestion/catch-up.js';
33
33
  import { CODEX_TOOL_SEMANTIC_MAPPINGS, CODEX_TOOL_SEMANTICS } from '../tool-semantics.js';
34
34
  const WORKER_OWNER = 'companion-worker';
@@ -407,6 +407,7 @@ export async function runCodexWorkspaceWorkerCycle(options) {
407
407
  logger,
408
408
  emitEvent,
409
409
  behaviorEvidence: createWorkspaceBehaviorEvidencePort(workspaceDir, CODEX_TOOL_SEMANTICS),
410
+ trustedFacts: createCodexArtificerTrustedFactsPort(),
410
411
  toolSemantics: CODEX_TOOL_SEMANTICS,
411
412
  // PRI-741: artificer generation gets the Codex host projection
412
413
  // (Bash/apply_patch — evidence-bound, intentionally sparse).
@@ -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",
@@ -23,6 +23,7 @@ import { handleApprovalsGroupedRoute, disposeApprovalsGroupedModels } from './ro
23
23
  import { handleGovernanceRoute, handleGovernanceExperienceRoute, resolveOwnerConfigSnapshot, disposeGovernanceModels } from './routes/governance.js';
24
24
  import { handleOwnerDecisionsRoute } from './routes/owner-decisions.js';
25
25
  import { handleEvidenceOutcomesRoute } from './routes/evidence-outcomes.js';
26
+ import { handleDesignGateRoute } from './routes/design-gate.js';
26
27
  import { handleOwnerDecisionInboxRoute, handleOwnerDecisionViewRoute, handleOwnerDecisionLibraryRoute, parseOwnerDecisionViewSubPath } from './routes/owner-decision.js';
27
28
  import { handleOwnerIdentityRoute } from './routes/owner-identity.js';
28
29
  import { handleEvidenceChainRoute, disposeEvidenceChainModels } from './routes/evidence-chain.js';
@@ -502,6 +503,17 @@ function handleRequest(services) {
502
503
  }))(req, res);
503
504
  return;
504
505
  }
506
+ if (urlPath.startsWith('/api/v1/governance/design-gate/')) {
507
+ const identity = resolveOwnerIdentity(process.env, defaultOwnerIdentityHomeDir());
508
+ const { ownerId, credentialId } = identity;
509
+ asyncHandler(() => handleDesignGateRoute(req, res, {
510
+ workspaceDir: services.workspaceDir,
511
+ subPath: urlPath.slice('/api/v1/governance/design-gate'.length),
512
+ ownerIdentity: services.authConfig.isEnabled() && ownerId !== null && credentialId !== null
513
+ ? { ownerId, credentialId } : null,
514
+ }))(req, res);
515
+ return;
516
+ }
505
517
  // PRI-466/477: GET/POST/PUT /api/v1/intent (+ /init, /content sub-paths)
506
518
  if (urlPath === '/api/v1/intent' || urlPath.startsWith('/api/v1/intent/')) {
507
519
  const subPath = urlPath === '/api/v1/intent' ? '' : urlPath.slice('/api/v1/intent'.length);
@@ -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 {
@@ -0,0 +1,11 @@
1
+ import type { IncomingMessage, ServerResponse } from 'node:http';
2
+ export interface DesignGateRouteContext {
3
+ workspaceDir: string;
4
+ subPath: string;
5
+ ownerIdentity: {
6
+ ownerId: string;
7
+ credentialId: string;
8
+ } | null;
9
+ }
10
+ /** Authenticated Owner attestations; never treats a principle approval as a design decision. */
11
+ export declare function handleDesignGateRoute(req: IncomingMessage, res: ServerResponse, ctx: DesignGateRouteContext): Promise<void>;
@@ -0,0 +1,156 @@
1
+ import { randomUUID } from 'node:crypto';
2
+ import path from 'node:path';
3
+ import { createRuntimeStateHandle, SqliteDesignGateStore, DesignGateStoreError } from '@principles/core/runtime-v2';
4
+ import { listObservedDesignGateSessions, readDesignGateSpecSnapshot } from '@principles/host-runtime';
5
+ import { readBody } from '../utils/request.js';
6
+ import { sendSuccess, sendError, sendBadRequest, sendMethodNotAllowed } from '../utils/response.js';
7
+ function record(value) {
8
+ return typeof value === 'object' && value !== null && !Array.isArray(value);
9
+ }
10
+ function text(value) {
11
+ return typeof value === 'string' && value.trim().length > 0 && value.length <= 4096;
12
+ }
13
+ function decision(value) {
14
+ return value === 'approved' || value === 'not_approved' || value === 'not_required';
15
+ }
16
+ function scope(value) {
17
+ return record(value) && Object.keys(value).every(key => key === 'paths' || key === 'actions')
18
+ && Array.isArray(value.paths) && value.paths.length > 0 && value.paths.length <= 100
19
+ && value.paths.every(p => text(p) && !/[*?[\]]/.test(p) && !path.isAbsolute(p) && !p.split(/[\\/]/).includes('..'))
20
+ && Array.isArray(value.actions) && value.actions.length > 0 && value.actions.length <= 3
21
+ && value.actions.every(a => a === 'read' || a === 'write' || a === 'execute');
22
+ }
23
+ function closedBody(value, keys) {
24
+ return record(value) && Object.keys(value).every(key => keys.includes(key));
25
+ }
26
+ /** Authenticated Owner attestations; never treats a principle approval as a design decision. */
27
+ export async function handleDesignGateRoute(req, res, ctx) {
28
+ if (!ctx.ownerIdentity) {
29
+ sendError(res, 403, 'owner_authentication_required', 'A verified Owner identity is required to read or change design declarations.');
30
+ return;
31
+ }
32
+ const url = new URL(req.url ?? '/', 'http://localhost');
33
+ if (req.method === 'GET' && ctx.subPath === '/sessions') {
34
+ try {
35
+ sendSuccess(res, { sessions: listObservedDesignGateSessions(ctx.workspaceDir) });
36
+ }
37
+ catch {
38
+ sendError(res, 503, 'session_source_unavailable', 'Observed Codex session data is unavailable.', { nextAction: 'Run a Codex session with PD observation enabled, then refresh.' });
39
+ }
40
+ return;
41
+ }
42
+ if (req.method === 'GET' && ctx.subPath === '/declaration') {
43
+ const project = url.searchParams.get('projectWorkspacePath');
44
+ const session = url.searchParams.get('hostSessionId');
45
+ if (!text(project) || !text(session)) {
46
+ sendBadRequest(res, 'Project and native session are required.');
47
+ return;
48
+ }
49
+ let declaration;
50
+ try {
51
+ const handle = await createRuntimeStateHandle({ workspaceDir: ctx.workspaceDir, readonly: true, bootstrapIfMissing: false });
52
+ try {
53
+ declaration = new SqliteDesignGateStore(handle.stateManager.connection).getLatestDeclaration(project, session);
54
+ }
55
+ finally {
56
+ await handle.close();
57
+ }
58
+ }
59
+ catch {
60
+ sendError(res, 503, 'declaration_source_unavailable', 'The design declaration source could not be read.', {
61
+ nextAction: 'Restore or initialize the governance workspace state database, then refresh the declaration.',
62
+ });
63
+ return;
64
+ }
65
+ sendSuccess(res, { declaration });
66
+ return;
67
+ }
68
+ if (req.method !== 'POST') {
69
+ sendMethodNotAllowed(res);
70
+ return;
71
+ }
72
+ let raw;
73
+ try {
74
+ raw = JSON.parse(await readBody(req));
75
+ }
76
+ catch {
77
+ sendBadRequest(res, 'Request body must be valid JSON.');
78
+ return;
79
+ }
80
+ const create = ctx.subPath === '/declarations';
81
+ const mutate = /^\/declarations\/([^/]+)\/(decision|revoke)$/.exec(ctx.subPath);
82
+ if (!create && !mutate) {
83
+ sendError(res, 404, 'not_found', 'Unknown design declaration operation.');
84
+ return;
85
+ }
86
+ if (create) {
87
+ if (!closedBody(raw, ['projectWorkspacePath', 'hostSessionId', 'specPath', 'scope', 'decision'])
88
+ || !text(raw.projectWorkspacePath) || !text(raw.hostSessionId) || !text(raw.specPath)
89
+ || !scope(raw.scope) || !decision(raw.decision)) {
90
+ sendBadRequest(res, 'Expected an observed project/session, SPEC path, explicit paths/actions and decision; identity fields are not accepted.');
91
+ return;
92
+ }
93
+ }
94
+ else if (!closedBody(raw, mutate?.[2] === 'decision' ? ['expectedRevision', 'decision'] : ['expectedRevision'])
95
+ || typeof raw.expectedRevision !== 'number' || !Number.isSafeInteger(raw.expectedRevision) || raw.expectedRevision < 1
96
+ || (mutate?.[2] === 'decision' && !decision(raw.decision))) {
97
+ sendBadRequest(res, 'Expected the current revision and a valid decision.');
98
+ return;
99
+ }
100
+ // All request validation/source checks precede any write transaction.
101
+ if (!record(raw)) {
102
+ sendBadRequest(res, 'Invalid declaration.');
103
+ return;
104
+ }
105
+ try {
106
+ let createInput = null;
107
+ if (create && text(raw.projectWorkspacePath) && text(raw.hostSessionId) && text(raw.specPath) && scope(raw.scope) && decision(raw.decision)) {
108
+ const chosen = listObservedDesignGateSessions(ctx.workspaceDir).find(entry => entry.projectWorkspacePath === raw.projectWorkspacePath && entry.hostSessionId === raw.hostSessionId);
109
+ if (!chosen) {
110
+ sendError(res, 409, 'native_scope_unverified', 'Select a Codex session already observed by this workspace.');
111
+ return;
112
+ }
113
+ const snapshot = readDesignGateSpecSnapshot(chosen.projectWorkspacePath, path.resolve(chosen.projectWorkspacePath, raw.specPath));
114
+ createInput = { project: chosen.projectWorkspacePath, session: chosen.hostSessionId, specPath: snapshot.realPath, specDigest: snapshot.digest, scope: raw.scope, decision: raw.decision };
115
+ }
116
+ const handle = await createRuntimeStateHandle({ workspaceDir: ctx.workspaceDir, bootstrapIfMissing: false });
117
+ try {
118
+ const store = new SqliteDesignGateStore(handle.stateManager.connection);
119
+ const actor = ctx.ownerIdentity;
120
+ const decidedAt = new Date().toISOString();
121
+ if (createInput) {
122
+ sendSuccess(res, { declaration: store.createDeclaration({ sourceRef: randomUUID(), governanceWorkspacePath: path.resolve(ctx.workspaceDir), projectWorkspacePath: createInput.project, hostSessionId: createInput.session, taskRef: `codex:${createInput.session}`, specPath: createInput.specPath, specDigest: createInput.specDigest, scope: createInput.scope, decision: createInput.decision, actor, decidedAt }) });
123
+ }
124
+ else if (mutate && typeof raw.expectedRevision === 'number') {
125
+ const sourceRef = decodeURIComponent(mutate[1] ?? '');
126
+ if (mutate[2] === 'revoke')
127
+ sendSuccess(res, { declaration: store.revoke({ sourceRef, expectedRevision: raw.expectedRevision, actor, decidedAt }) });
128
+ else if (decision(raw.decision)) {
129
+ const existing = store.getDeclaration(sourceRef);
130
+ if (existing) {
131
+ const currentSpec = readDesignGateSpecSnapshot(existing.projectWorkspacePath, existing.specPath);
132
+ if (currentSpec.digest !== existing.specDigest) {
133
+ sendError(res, 409, 'design_gate_spec_changed', 'The bound SPEC changed after this declaration was recorded.', {
134
+ nextAction: 'Review the current SPEC and create a new declaration before changing its decision.',
135
+ });
136
+ return;
137
+ }
138
+ }
139
+ sendSuccess(res, { declaration: store.updateDecision({ sourceRef, expectedRevision: raw.expectedRevision, decision: raw.decision, actor, decidedAt }) });
140
+ }
141
+ }
142
+ }
143
+ finally {
144
+ await handle.close();
145
+ }
146
+ }
147
+ catch (error) {
148
+ if (error instanceof DesignGateStoreError) {
149
+ const status = error.code === 'invalid_input' ? 400 : error.code === 'not_found' ? 404 : error.code === 'corrupt_data' ? 503 : 409;
150
+ sendError(res, status, `design_gate_${error.code}`, error.message, { nextAction: 'Refresh the declaration before retrying; no previous record was overwritten.' });
151
+ }
152
+ else {
153
+ sendError(res, 503, 'design_gate_source_unavailable', 'The SPEC, observed session or declaration source could not be verified.', { nextAction: 'Check that the SPEC is a readable file inside the observed project and refresh the session list.' });
154
+ }
155
+ }
156
+ }
@@ -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
@@ -1,5 +1,6 @@
1
1
  import type { ApiResponse } from "../types.js";
2
2
  import type { OwnerDecisionsData, OwnerResolutionResultData, OwnerDecisionInboxData, OwnerDecisionLibraryData } from "./utils/validators.js";
3
+ import type { DesignGateAction, DesignGateDecision, DesignGateDeclaration, DesignGateScope } from "@principles/core/runtime-v2/internalization/design-gate-facts";
3
4
  import type { GovernanceExperienceSnapshot, OwnerGovernanceView, OwnerDecisionViewCore } from '@principles/core/runtime-v2';
4
5
  import type { OwnerIdentityViewData, OwnerIdentityRegisterData, OwnerIdentityUnregisterData } from "./utils/validators.js";
5
6
  import type { FeedbackReportData, FeedbackDraftSummaryData, FeedbackDraftEnvelopeData, FeedbackChannelsData, FeedbackSubmitResultData, DeleteEnvelopeData, WorkspaceEntryData, RemovedEnvelopeData, SyncResultData, ConfigSummaryData, ConfigCatalogData, AgentBindingUpdateData, DefaultRuntimeUpdateData, RuntimeProfileMutationData, ReadinessCheckData, FeatureFlagUpdateData, OutputLanguageData, GovernanceQueueData, RecoveryResultData, FailedTaskDetailData, ActivationsData, DisableActivationData, LifecycleMetricsData, UpdateStatusData, UpdateHistoryData, ApplyUpdateResultData, UpdateRecoveryData, UpdateTransactionData, ApprovalRecordData, PrinciplesListData, CorePrinciplesData, ApprovalsGroupedData, EvidenceChainData, TrajectoryData, IntentSummaryData, IntentDecisionRecordData, IntentDecisionResultData, IntentDecisionSummaryData, FollowUpResponseData, IntentInitResultData, IntentSaveResultData, IntentRawContentData, IntentVersionData, RuleCodeOwnerReviewData, RuleCodeMutationData } from "./utils/validators.js";
@@ -343,7 +344,34 @@ export interface FollowUpPayload {
343
344
  candidateId?: string;
344
345
  }
345
346
  declare function dispatchFollowUp(decisionId: string, payload: FollowUpPayload): Promise<ApiResponse<FollowUpResponseData>>;
346
- export { request, getToken, setToken, clearToken, checkAuth, approveApproval, rejectApproval, editApproval, fetchPrinciples, fetchCorePrinciples, fetchPrincipleDetail, fetchPrincipleGovernance, fetchOwnerDecisionView, fetchOwnerDecisionInbox, fetchOwnerDecisionLibrary, fetchPrincipleTrajectory, fetchPrincipleReceipts, fetchReceiptCounts, fetchEvidenceAudit, archivePrinciple, unarchivePrinciple, createFeedbackReport, listFeedbackReports, getFeedbackReport, deleteFeedbackReport, fetchFeedbackChannels, submitFeedbackReport, markFeedbackReportSent, fetchConfigSummary, fetchConfigCatalog, updateAgentBinding, updateDefaultRuntime, createRuntimeProfile, updateRuntimeProfile, deleteRuntimeProfile, fetchAgentReadiness, patchFeatureFlag, fetchOutputLanguage, updateOutputLanguage, fetchWorkspaces, addWorkspace, removeWorkspace, syncWorkspace, fetchGovernanceQueue, fetchGovernanceExperience, recoverFailedTask, fetchFailedTaskDetail, fetchOwnerDecisions, resolveOwnerDecision, fetchApprovalsGrouped, fetchAllActivations, disableActivation, proposePromptRevision, fetchRuleCodeOwnerReview, ruleCodeDecision, pauseAllRuleCode, releaseRuleCodePause, fetchLifecycleMetrics, fetchUpdateStatus, fetchUpdateHistory, applyFullUpdate, fetchUpdateRecovery, fetchUpdateTransaction, fetchEvidenceChain, fetchIntentSummary, fetchIntentContent, createIntentTemplate, saveIntentContent, fetchIntentVersions, recordIntentDecision, listIntentDecisionsByPainId, listIntentDecisionsByTaskId, fetchIntentDecisionSummary, dispatchFollowUp, };
347
+ export interface DesignGateObservedSession {
348
+ projectWorkspacePath: string;
349
+ hostSessionId: string;
350
+ }
351
+ export interface DesignGateDeclarationInput {
352
+ projectWorkspacePath: string;
353
+ hostSessionId: string;
354
+ specPath: string;
355
+ scope: DesignGateScope;
356
+ decision: DesignGateDecision;
357
+ }
358
+ declare function fetchDesignGateSessions(): Promise<ApiResponse<{
359
+ sessions: DesignGateObservedSession[];
360
+ }>>;
361
+ declare function fetchDesignGateDeclaration(projectWorkspacePath: string, hostSessionId: string): Promise<ApiResponse<{
362
+ declaration: DesignGateDeclaration | null;
363
+ }>>;
364
+ declare function createDesignGateDeclaration(input: DesignGateDeclarationInput): Promise<ApiResponse<{
365
+ declaration: DesignGateDeclaration | null;
366
+ }>>;
367
+ declare function updateDesignGateDecision(sourceRef: string, expectedRevision: number, decision: DesignGateDecision): Promise<ApiResponse<{
368
+ declaration: DesignGateDeclaration | null;
369
+ }>>;
370
+ declare function revokeDesignGateDeclaration(sourceRef: string, expectedRevision: number): Promise<ApiResponse<{
371
+ declaration: DesignGateDeclaration | null;
372
+ }>>;
373
+ export { request, getToken, setToken, clearToken, checkAuth, approveApproval, rejectApproval, editApproval, fetchPrinciples, fetchCorePrinciples, fetchPrincipleDetail, fetchPrincipleGovernance, fetchOwnerDecisionView, fetchOwnerDecisionInbox, fetchOwnerDecisionLibrary, fetchPrincipleTrajectory, fetchPrincipleReceipts, fetchReceiptCounts, fetchEvidenceAudit, archivePrinciple, unarchivePrinciple, createFeedbackReport, listFeedbackReports, getFeedbackReport, deleteFeedbackReport, fetchFeedbackChannels, submitFeedbackReport, markFeedbackReportSent, fetchConfigSummary, fetchConfigCatalog, updateAgentBinding, updateDefaultRuntime, createRuntimeProfile, updateRuntimeProfile, deleteRuntimeProfile, fetchAgentReadiness, patchFeatureFlag, fetchOutputLanguage, updateOutputLanguage, fetchWorkspaces, addWorkspace, removeWorkspace, syncWorkspace, fetchGovernanceQueue, fetchGovernanceExperience, recoverFailedTask, fetchFailedTaskDetail, fetchOwnerDecisions, resolveOwnerDecision, fetchApprovalsGrouped, fetchAllActivations, disableActivation, proposePromptRevision, fetchRuleCodeOwnerReview, ruleCodeDecision, pauseAllRuleCode, releaseRuleCodePause, fetchLifecycleMetrics, fetchUpdateStatus, fetchUpdateHistory, applyFullUpdate, fetchUpdateRecovery, fetchUpdateTransaction, fetchEvidenceChain, fetchIntentSummary, fetchIntentContent, createIntentTemplate, saveIntentContent, fetchIntentVersions, recordIntentDecision, listIntentDecisionsByPainId, listIntentDecisionsByTaskId, fetchIntentDecisionSummary, dispatchFollowUp, fetchDesignGateSessions, fetchDesignGateDeclaration, createDesignGateDeclaration, updateDesignGateDecision, revokeDesignGateDeclaration, };
374
+ export type { DesignGateAction, DesignGateDecision, DesignGateDeclaration, DesignGateScope };
347
375
  export type { FeedbackReportData, FeedbackDraftSummaryData, FeedbackDraftEnvelopeData, FeedbackChannelsData, FeedbackSubmitResultData, DeleteEnvelopeData, WorkspaceEntryData, RemovedEnvelopeData, SyncResultData, ConfigSummaryData, ConfigCatalogData, AgentBindingUpdateData, DefaultRuntimeUpdateData, RuntimeProfileMutationData, ReadinessCheckData, FeatureFlagUpdateData, OutputLanguageData, GovernanceQueueData, RecoveryResultData, ActivationsData, DisableActivationData, RuleCodeOwnerReviewData, RuleCodeMutationData, FailedTaskDetailData, LifecycleMetricsData, UpdateStatusData, UpdateHistoryData, ApplyUpdateResultData, UpdateRecoveryData, ApprovalRecordData, PrinciplesListData, CorePrinciplesData, CorePrincipleData, ApprovalsGroupedData, EvidenceChainData, EvidenceChainRecordData, EvidenceChainStateData, TrajectoryData, TrajectoryStageData, IntentSummaryData, IntentSectionsData, IntentDocWarningData, IntentDecisionRecordData, IntentDecisionResultData, IntentDecisionSummaryData, FollowUpResponseData, LinkCandidateFollowUpData, GuideRulehostFollowUpData, GeneratePatchProposalFollowUpData, IntentVersionEntry, IntentVersionData, } from "./utils/validators.js";
348
376
  export type { ActivationRecordData as ActivationRecord } from "./utils/validators.js";
349
377
  export type { OwnerDecisionInboxData, OwnerDecisionLibraryData } from "./utils/validators.js";
@@ -891,9 +891,113 @@ async function dispatchFollowUp(decisionId, payload) {
891
891
  body: JSON.stringify(payload),
892
892
  }, validateFollowUpResponse);
893
893
  }
894
+ function isDesignGateRecord(value) {
895
+ return typeof value === "object" && value !== null && !Array.isArray(value);
896
+ }
897
+ function validateDesignGateSessions(value) {
898
+ if (!isDesignGateRecord(value) || !Array.isArray(value.sessions))
899
+ return null;
900
+ const sessions = [];
901
+ for (const entry of value.sessions) {
902
+ if (!isDesignGateRecord(entry)
903
+ || typeof entry.projectWorkspacePath !== "string" || entry.projectWorkspacePath.trim() === ""
904
+ || typeof entry.hostSessionId !== "string" || entry.hostSessionId.trim() === "")
905
+ return null;
906
+ sessions.push({ projectWorkspacePath: entry.projectWorkspacePath, hostSessionId: entry.hostSessionId });
907
+ }
908
+ return { sessions };
909
+ }
910
+ function isDesignGateAction(value) {
911
+ return value === "read" || value === "write" || value === "execute";
912
+ }
913
+ function isDesignGateDecisionValue(value) {
914
+ return value === "approved" || value === "not_approved" || value === "not_required";
915
+ }
916
+ function isNonEmptyDesignGateString(value) {
917
+ return typeof value === "string" && value.trim().length > 0;
918
+ }
919
+ function isDesignGateStringArray(value) {
920
+ if (!Array.isArray(value))
921
+ return false;
922
+ for (let index = 0; index < value.length; index += 1) {
923
+ if (!Object.hasOwn(value, index) || !isNonEmptyDesignGateString(value[index]))
924
+ return false;
925
+ }
926
+ return true;
927
+ }
928
+ /**
929
+ * Field-by-field mirror of the server declaration contract (rc-1 style) —
930
+ * the browser bundle never runtime-imports the Node-oriented core barrel, so
931
+ * the wire format is re-validated locally; the server remains the authority.
932
+ */
933
+ function isDesignGateDeclarationValue(value) {
934
+ if (!isDesignGateRecord(value))
935
+ return false;
936
+ if (!Object.hasOwn(value, "sourceRef") || !isNonEmptyDesignGateString(value.sourceRef))
937
+ return false;
938
+ if (!isNonEmptyDesignGateString(value.governanceWorkspacePath))
939
+ return false;
940
+ if (!isNonEmptyDesignGateString(value.projectWorkspacePath))
941
+ return false;
942
+ if (!isNonEmptyDesignGateString(value.hostSessionId))
943
+ return false;
944
+ if (!isNonEmptyDesignGateString(value.taskRef))
945
+ return false;
946
+ if (!isNonEmptyDesignGateString(value.specPath) || !isNonEmptyDesignGateString(value.specDigest))
947
+ return false;
948
+ if (!isDesignGateRecord(value.scope))
949
+ return false;
950
+ if (!Object.hasOwn(value.scope, "paths") || !Object.hasOwn(value.scope, "actions"))
951
+ return false;
952
+ if (!isDesignGateStringArray(value.scope.paths) || value.scope.paths.length === 0)
953
+ return false;
954
+ if (!Array.isArray(value.scope.actions) || value.scope.actions.length === 0)
955
+ return false;
956
+ for (let index = 0; index < value.scope.actions.length; index += 1) {
957
+ if (!Object.hasOwn(value.scope.actions, index) || !isDesignGateAction(value.scope.actions[index]))
958
+ return false;
959
+ }
960
+ if (!isDesignGateDecisionValue(value.decision))
961
+ return false;
962
+ if (typeof value.revision !== "number" || !Number.isSafeInteger(value.revision) || value.revision < 1)
963
+ return false;
964
+ if (typeof value.revoked !== "boolean")
965
+ return false;
966
+ if (!isDesignGateRecord(value.actor)
967
+ || !Object.hasOwn(value.actor, "ownerId") || !Object.hasOwn(value.actor, "credentialId")
968
+ || !isNonEmptyDesignGateString(value.actor.ownerId) || !isNonEmptyDesignGateString(value.actor.credentialId))
969
+ return false;
970
+ if (!isNonEmptyDesignGateString(value.decidedAt))
971
+ return false;
972
+ return true;
973
+ }
974
+ function validateDesignGateDeclarationEnvelope(value) {
975
+ if (!isDesignGateRecord(value) || !Object.hasOwn(value, "declaration"))
976
+ return null;
977
+ if (value.declaration === null)
978
+ return { declaration: null };
979
+ return isDesignGateDeclarationValue(value.declaration) ? { declaration: value.declaration } : null;
980
+ }
981
+ const designGateBasePath = "/api/v1/governance/design-gate";
982
+ async function fetchDesignGateSessions() {
983
+ return request(`${designGateBasePath}/sessions`, undefined, validateDesignGateSessions);
984
+ }
985
+ async function fetchDesignGateDeclaration(projectWorkspacePath, hostSessionId) {
986
+ const query = new URLSearchParams({ projectWorkspacePath, hostSessionId });
987
+ return request(`${designGateBasePath}/declaration?${query.toString()}`, undefined, validateDesignGateDeclarationEnvelope);
988
+ }
989
+ async function createDesignGateDeclaration(input) {
990
+ return request(`${designGateBasePath}/declarations`, { method: "POST", body: JSON.stringify(input) }, validateDesignGateDeclarationEnvelope);
991
+ }
992
+ async function updateDesignGateDecision(sourceRef, expectedRevision, decision) {
993
+ return request(`${designGateBasePath}/declarations/${encodeURIComponent(sourceRef)}/decision`, { method: "POST", body: JSON.stringify({ expectedRevision, decision }) }, validateDesignGateDeclarationEnvelope);
994
+ }
995
+ async function revokeDesignGateDeclaration(sourceRef, expectedRevision) {
996
+ return request(`${designGateBasePath}/declarations/${encodeURIComponent(sourceRef)}/revoke`, { method: "POST", body: JSON.stringify({ expectedRevision }) }, validateDesignGateDeclarationEnvelope);
997
+ }
894
998
  // ── Exports ───────────────────────────────────────────────────────────────────
895
999
  export { request,
896
1000
  // auth
897
1001
  getToken, setToken, clearToken, checkAuth, approveApproval, rejectApproval, editApproval, fetchPrinciples, fetchCorePrinciples, fetchPrincipleDetail, fetchPrincipleGovernance, fetchOwnerDecisionView, fetchOwnerDecisionInbox, fetchOwnerDecisionLibrary, fetchPrincipleTrajectory, fetchPrincipleReceipts, fetchReceiptCounts, fetchEvidenceAudit, archivePrinciple, unarchivePrinciple, createFeedbackReport, listFeedbackReports, getFeedbackReport, deleteFeedbackReport,
898
1002
  // Feedback submit ladder (Slice 3, spec §8)
899
- fetchFeedbackChannels, submitFeedbackReport, markFeedbackReportSent, fetchConfigSummary, fetchConfigCatalog, updateAgentBinding, updateDefaultRuntime, createRuntimeProfile, updateRuntimeProfile, deleteRuntimeProfile, fetchAgentReadiness, patchFeatureFlag, fetchOutputLanguage, updateOutputLanguage, fetchWorkspaces, addWorkspace, removeWorkspace, syncWorkspace, fetchGovernanceQueue, fetchGovernanceExperience, recoverFailedTask, fetchFailedTaskDetail, fetchOwnerDecisions, resolveOwnerDecision, fetchApprovalsGrouped, fetchAllActivations, disableActivation, proposePromptRevision, fetchRuleCodeOwnerReview, ruleCodeDecision, pauseAllRuleCode, releaseRuleCodePause, fetchLifecycleMetrics, fetchUpdateStatus, fetchUpdateHistory, applyFullUpdate, fetchUpdateRecovery, fetchUpdateTransaction, fetchEvidenceChain, fetchIntentSummary, fetchIntentContent, createIntentTemplate, saveIntentContent, fetchIntentVersions, recordIntentDecision, listIntentDecisionsByPainId, listIntentDecisionsByTaskId, fetchIntentDecisionSummary, dispatchFollowUp, };
1003
+ fetchFeedbackChannels, submitFeedbackReport, markFeedbackReportSent, fetchConfigSummary, fetchConfigCatalog, updateAgentBinding, updateDefaultRuntime, createRuntimeProfile, updateRuntimeProfile, deleteRuntimeProfile, fetchAgentReadiness, patchFeatureFlag, fetchOutputLanguage, updateOutputLanguage, fetchWorkspaces, addWorkspace, removeWorkspace, syncWorkspace, fetchGovernanceQueue, fetchGovernanceExperience, recoverFailedTask, fetchFailedTaskDetail, fetchOwnerDecisions, resolveOwnerDecision, fetchApprovalsGrouped, fetchAllActivations, disableActivation, proposePromptRevision, fetchRuleCodeOwnerReview, ruleCodeDecision, pauseAllRuleCode, releaseRuleCodePause, fetchLifecycleMetrics, fetchUpdateStatus, fetchUpdateHistory, applyFullUpdate, fetchUpdateRecovery, fetchUpdateTransaction, fetchEvidenceChain, fetchIntentSummary, fetchIntentContent, createIntentTemplate, saveIntentContent, fetchIntentVersions, recordIntentDecision, listIntentDecisionsByPainId, listIntentDecisionsByTaskId, fetchIntentDecisionSummary, dispatchFollowUp, fetchDesignGateSessions, fetchDesignGateDeclaration, createDesignGateDeclaration, updateDesignGateDecision, revokeDesignGateDeclaration, };