@hassangameryt/freeflow 0.6.0 → 0.7.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (136) hide show
  1. package/.claude-plugin/marketplace.json +1 -1
  2. package/.claude-plugin/plugin.json +3 -2
  3. package/.codex-plugin/plugin.json +5 -6
  4. package/.cursor-plugin/plugin.json +25 -0
  5. package/README.md +66 -56
  6. package/capabilities/cognitive-routing/SKILL.md +288 -130
  7. package/com.github.copilot/hooks/hooks.json +11 -0
  8. package/command-surface.json +0 -24
  9. package/gemini-extension.json +5 -0
  10. package/hooks/adapters/claude-session-start.mjs +16 -0
  11. package/hooks/adapters/codex-session-start.mjs +16 -0
  12. package/hooks/adapters/copilot-session-start.mjs +13 -0
  13. package/hooks/adapters/cursor-session-start.mjs +13 -0
  14. package/hooks/adapters/gemini-session-start.mjs +15 -0
  15. package/hooks/claude/hooks.json +18 -0
  16. package/hooks/codex/hooks.json +18 -0
  17. package/hooks/cursor/hooks.json +9 -0
  18. package/hooks/freeflow-runtime-context.mjs +3 -650
  19. package/hooks/hooks.json +4 -30
  20. package/hooks/shared/runtime-context.mjs +210 -0
  21. package/hooks/tests/check-host-adapters.test.mjs +82 -0
  22. package/hooks/tests/check-runtime-context-hook.sh +140 -320
  23. package/package.json +20 -4
  24. package/pi-extension/dist/cognitive-routing/commands.js +3 -3
  25. package/pi-extension/dist/cognitive-routing/config.js +16 -20
  26. package/pi-extension/dist/cognitive-routing/controller.js +478 -48
  27. package/pi-extension/dist/cognitive-routing/history.js +12 -0
  28. package/pi-extension/dist/cognitive-routing/host.js +74 -12
  29. package/pi-extension/dist/cognitive-routing/pi-routing-session.js +414 -0
  30. package/pi-extension/dist/cognitive-routing/pi-session-control.js +320 -0
  31. package/pi-extension/dist/cognitive-routing/tool.js +1 -0
  32. package/pi-extension/dist/cognitive-routing/types.js +1 -1
  33. package/pi-extension/dist/index.js +236 -219
  34. package/pi-extension/dist/runtime/runtime-context.js +126 -286
  35. package/pi-extension/dist/settings/settings-ui.js +52 -336
  36. package/pi-extension/src/cognitive-routing/commands.ts +15 -5
  37. package/pi-extension/src/cognitive-routing/config.ts +16 -20
  38. package/pi-extension/src/cognitive-routing/controller.ts +561 -51
  39. package/pi-extension/src/cognitive-routing/history.ts +12 -0
  40. package/pi-extension/src/cognitive-routing/host.ts +110 -16
  41. package/pi-extension/src/cognitive-routing/pi-routing-session.ts +538 -0
  42. package/pi-extension/src/cognitive-routing/pi-session-control.ts +444 -0
  43. package/pi-extension/src/cognitive-routing/tool.ts +1 -0
  44. package/pi-extension/src/cognitive-routing/types.ts +1 -1
  45. package/pi-extension/src/index.ts +249 -239
  46. package/pi-extension/src/runtime/runtime-context.ts +129 -305
  47. package/pi-extension/src/settings/settings-ui.ts +53 -371
  48. package/pi-extension/tests/cognitive-routing/commands.test.js +1 -1
  49. package/pi-extension/tests/cognitive-routing/controller.test.js +69 -40
  50. package/pi-extension/tests/cognitive-routing/extension-lifecycle.test.js +128 -57
  51. package/pi-extension/tests/cognitive-routing/pi-normal-lifecycle.test.js +434 -0
  52. package/pi-extension/tests/cognitive-routing/pi-session-control.test.js +174 -0
  53. package/pi-extension/tests/cognitive-routing/pi-session-history.test.js +91 -0
  54. package/pi-extension/tests/cognitive-routing/profile-resolution.test.js +47 -4
  55. package/pi-extension/tests/integration/mode-free-core.test.js +218 -0
  56. package/pi-extension/tests/integration/pi-extension.test.js +430 -2478
  57. package/pi-extension/tests/integration/prompt-architecture.test.js +124 -79
  58. package/pi-extension/tests/integration/unaffected-regressions.test.js +411 -0
  59. package/plugin.json +33 -0
  60. package/runtime/prompts/cognitive-routing.md +5 -4
  61. package/runtime/prompts/conversation-history.md +1 -1
  62. package/runtime/prompts/core.md +77 -6
  63. package/runtime/prompts/interaction-contract.md +6 -2
  64. package/skills/action-selection/SKILL.md +93 -44
  65. package/skills/action-selection/references/trajectory-stalls.md +71 -0
  66. package/skills/bypass/SKILL.md +109 -39
  67. package/skills/commit-work/SKILL.md +94 -55
  68. package/skills/commit-work/references/git-state-edges.md +94 -0
  69. package/skills/commit-work/references/simple-push.md +92 -0
  70. package/skills/commit-work/references/staging-decisions.md +79 -46
  71. package/skills/decision-gate/SKILL.md +80 -22
  72. package/skills/design-for-depth/SKILL.md +208 -43
  73. package/skills/design-for-depth/references/design-pressure-signals.md +108 -240
  74. package/skills/design-for-depth/references/interface-design-loop.md +77 -97
  75. package/skills/design-for-depth/references/module-and-dependency-design.md +147 -0
  76. package/skills/design-for-depth/references/software-design-philosophy.md +127 -173
  77. package/skills/design-for-depth/references/state-and-failure-boundaries.md +136 -0
  78. package/skills/diagnose-failure/SKILL.md +198 -52
  79. package/skills/diagnose-failure/references/diagnostic-loop-catalog.md +19 -5
  80. package/skills/diagnose-failure/references/flaky-and-performance-diagnosis.md +13 -5
  81. package/skills/discuss/SKILL.md +118 -76
  82. package/skills/discuss/references/discussion-continuity.md +84 -32
  83. package/skills/evaluate-skill/SKILL.md +166 -52
  84. package/skills/evaluate-skill/references/evaluation-definition-schema.md +2 -2
  85. package/skills/evaluate-skill/references/evaluation-design.md +56 -30
  86. package/skills/evaluate-skill/references/execution-and-evidence.md +1 -1
  87. package/skills/evaluate-skill/references/review-and-revision.md +62 -21
  88. package/skills/execute-work/SKILL.md +98 -59
  89. package/skills/execute-work/references/code-practices.md +4 -4
  90. package/skills/execute-work/references/execute-work-edges.md +42 -22
  91. package/skills/review-artifact/SKILL.md +132 -71
  92. package/skills/review-artifact/references/adjudicate-artifact-review.md +101 -0
  93. package/skills/review-artifact/references/independent-artifact-reviewer-contract.md +52 -41
  94. package/skills/review-work/SKILL.md +115 -72
  95. package/skills/review-work/references/adjudicate-work-review.md +97 -0
  96. package/skills/review-work/references/independent-work-reviewer-contract.md +40 -35
  97. package/skills/review-work/references/security-risk-lens.md +17 -15
  98. package/skills/setup-freeflow/SKILL.md +14 -17
  99. package/skills/setup-freeflow/references/activation-contract.md +24 -30
  100. package/skills/setup-freeflow/references/host-setup.md +6 -7
  101. package/skills/simplify-code/SKILL.md +97 -39
  102. package/skills/simplify-code/references/simplification-patterns.md +1 -1
  103. package/skills/tdd/SKILL.md +108 -41
  104. package/skills/tdd/references/test-design.md +1 -1
  105. package/skills/track-work/SKILL.md +133 -300
  106. package/skills/track-work/references/working-record-format.md +515 -0
  107. package/skills/track-work/scripts/lib/document.mjs +713 -0
  108. package/skills/track-work/scripts/lib/format.mjs +166 -0
  109. package/skills/track-work/scripts/lib/store.mjs +233 -0
  110. package/skills/track-work/scripts/lib/transitions.mjs +790 -0
  111. package/skills/track-work/scripts/lib/views.mjs +27 -430
  112. package/skills/track-work/scripts/working-record.mjs +292 -98
  113. package/skills/verify-work/SKILL.md +46 -20
  114. package/skills/verify-work/references/browser-runtime-evidence.md +1 -1
  115. package/skills/verify-work/references/integration-evidence.md +1 -7
  116. package/skills/verify-work/references/performance-evidence.md +1 -1
  117. package/skills/workflow/SKILL.md +110 -80
  118. package/skills/workflow/references/domain-skill-composition.md +24 -18
  119. package/skills/write-skill/SKILL.md +218 -62
  120. package/skills/write-skill/references/activation-boundaries.md +63 -21
  121. package/skills/write-skill/references/agent-first-instructions.md +61 -27
  122. package/skills/write-skill/references/progressive-disclosure.md +91 -41
  123. package/skills/write-skill/references/skill-dependency-graphs.md +190 -0
  124. package/runtime/prompts/skills.md +0 -65
  125. package/skills/mode-contract/SKILL.md +0 -104
  126. package/skills/track-work/scripts/lib/codec.mjs +0 -570
  127. package/skills/track-work/scripts/lib/commands.mjs +0 -568
  128. package/skills/track-work/scripts/lib/contract.mjs +0 -303
  129. package/skills/track-work/scripts/lib/model.mjs +0 -506
  130. package/skills/track-work/scripts/lib/operations.mjs +0 -965
  131. package/skills/track-work/scripts/lib/record-store.mjs +0 -264
  132. package/skills/track-work/scripts/lib/result.mjs +0 -37
  133. package/skills/track-work/scripts/lib/rewrites.mjs +0 -740
  134. package/skills/track-work/scripts/lib/schema.mjs +0 -277
  135. package/skills/track-work/scripts/lib/source-inventory.mjs +0 -162
  136. package/skills/track-work/scripts/lib/workspace.mjs +0 -134
@@ -5,14 +5,17 @@ import {
5
5
  type CognitiveRoutingHistoryOptions,
6
6
  type CognitiveRoutingHistoryResult,
7
7
  } from "./history.js";
8
+ import { PI_SESSION_MODEL_STATE_ENTRY, parsePiSessionModelStateCommit } from "./pi-session-control.js";
8
9
  import type {
9
10
  CognitiveRoutingCapabilityState,
10
11
  CognitiveRoutingProfileName,
12
+ CognitiveRoutingSessionStart,
11
13
  CognitiveRoutingThinkingLevel,
12
14
  } from "./types.js";
13
15
 
14
16
  export const COGNITIVE_ROUTING_INTENT_ENTRY = "freeflow-cognitive-routing-intent";
15
17
  export const COGNITIVE_ROUTING_CONTROL_ENTRY = "freeflow-cognitive-routing-control";
18
+ export const COGNITIVE_ROUTING_INACTIVE_ENTRY = "freeflow-cognitive-routing-inactive";
16
19
 
17
20
  export type CognitiveRoutingMechanism =
18
21
  | "agent-tool"
@@ -46,6 +49,7 @@ type CognitiveRoutingIntent = {
46
49
  decisionCorrelationId?: string;
47
50
  fromPair?: CognitiveRoutingPair;
48
51
  fromProfile?: CognitiveRoutingProfileName;
52
+ fromControl?: "automatic" | "manual";
49
53
  profile?: CognitiveRoutingProfileName;
50
54
  resumeProfile?: CognitiveRoutingProfileName;
51
55
  resumeControl?: "automatic" | "manual";
@@ -64,7 +68,16 @@ type CognitiveRoutingControlEntry = {
64
68
  epoch: string;
65
69
  };
66
70
 
67
- type CognitiveRoutingPersistedData = CognitiveRoutingIntent | CognitiveRoutingControlEntry;
71
+ type CognitiveRoutingInactiveEntry = {
72
+ version: 1;
73
+ source: "user";
74
+ reason: string;
75
+ branchId?: string | null;
76
+ pair: CognitiveRoutingPair;
77
+ };
78
+
79
+ type CognitiveRoutingPersistedData =
80
+ CognitiveRoutingIntent | CognitiveRoutingControlEntry | CognitiveRoutingInactiveEntry;
68
81
 
69
82
  type ModelStateLease = {
70
83
  setState(request: CognitiveRoutingPair & { correlationId: string }): Promise<{ status: string }>;
@@ -77,6 +90,11 @@ type ModelStateAcquisition =
77
90
  type ControllerPi = {
78
91
  appendEntryDurable(customType: string, data: CognitiveRoutingPersistedData): void;
79
92
  acquireModelStateControl(options: { label: string }): Promise<ModelStateAcquisition>;
93
+ recoverPreparedModelState?: (request: {
94
+ fromPair: CognitiveRoutingPair;
95
+ target: CognitiveRoutingPair;
96
+ correlationId: string;
97
+ }) => Promise<{ status: "restored" | "rollback-failed" | "rejected" }>;
80
98
  };
81
99
 
82
100
  type ControllerContext = {
@@ -93,15 +111,24 @@ type ControllerOptions = {
93
111
  capabilityState: CognitiveRoutingCapabilityState;
94
112
  pi: ControllerPi;
95
113
  ctx: ControllerContext;
114
+ initialSessionStart?: CognitiveRoutingSessionStart;
96
115
  label?: string;
97
116
  idFactory?: () => string;
117
+ liveStateAuthoritative?: boolean;
98
118
  };
99
119
 
100
120
  export type CognitiveRoutingControlMode = "automatic" | "manual-standard" | "manual-reasoning";
101
121
 
122
+ export type CognitiveRoutingRuntimeStatus = "active" | "inactive" | "blocked";
123
+
124
+ export type CognitiveRoutingNativeChangeResult =
125
+ { status: "ignored" } | { status: "inactive"; reason: string } | { status: "blocked"; reason: string };
126
+
102
127
  export type CognitiveRoutingControllerState = {
103
128
  effective: boolean;
104
129
  controlMode: CognitiveRoutingControlMode;
130
+ runtimeStatus?: CognitiveRoutingRuntimeStatus;
131
+ runtimeReason?: string;
105
132
  activeProfile?: CognitiveRoutingProfileName;
106
133
  epoch?: string;
107
134
  returnTarget?: CognitiveRoutingPair;
@@ -187,18 +214,47 @@ function asPair(value: unknown): CognitiveRoutingPair | undefined {
187
214
  };
188
215
  }
189
216
 
217
+ function hostPairFromEntry(entry: Record<string, unknown>): CognitiveRoutingPair | undefined {
218
+ if (entry.type === "model_state_change") {
219
+ return asPair({
220
+ provider: entry.provider,
221
+ modelId: entry.modelId,
222
+ thinkingLevel: entry.thinkingLevel,
223
+ });
224
+ }
225
+ if (entry.type !== "custom" || entry.customType !== PI_SESSION_MODEL_STATE_ENTRY) return undefined;
226
+ const commit = parsePiSessionModelStateCommit(entry.data);
227
+ if (!commit || commit.phase !== "committed" || commit.status !== "applied") return undefined;
228
+ return commit.target;
229
+ }
230
+
231
+ function hostCorrelationIdFromEntry(entry: Record<string, unknown>): string | undefined {
232
+ if (entry.type === "model_state_change") {
233
+ return typeof entry.correlationId === "string" ? entry.correlationId : undefined;
234
+ }
235
+ if (entry.type !== "custom" || entry.customType !== PI_SESSION_MODEL_STATE_ENTRY) return undefined;
236
+ const commit = parsePiSessionModelStateCommit(entry.data);
237
+ return commit?.phase === "committed" && commit.status === "applied" ? commit.correlationId : undefined;
238
+ }
239
+
240
+ function latestPiSessionCommit(entries: readonly unknown[], correlationId: string) {
241
+ for (let index = entries.length - 1; index >= 0; index -= 1) {
242
+ const entry = entries[index];
243
+ if (!entry || typeof entry !== "object") continue;
244
+ const candidate = entry as Record<string, unknown>;
245
+ if (candidate.type !== "custom" || candidate.customType !== PI_SESSION_MODEL_STATE_ENTRY) continue;
246
+ const commit = parsePiSessionModelStateCommit(candidate.data);
247
+ if (commit?.correlationId === correlationId) return commit;
248
+ }
249
+ return undefined;
250
+ }
251
+
190
252
  function latestHostPair(context: ControllerContext): CognitiveRoutingPair | undefined {
191
253
  const entries = branchEntriesFrom(context);
192
254
  for (let index = entries.length - 1; index >= 0; index -= 1) {
193
255
  const entry = entries[index];
194
256
  if (!entry || typeof entry !== "object") continue;
195
- const candidate = entry as Record<string, unknown>;
196
- if (candidate.type !== "model_state_change") continue;
197
- const pair = asPair({
198
- provider: candidate.provider,
199
- modelId: candidate.modelId,
200
- thinkingLevel: candidate.thinkingLevel,
201
- });
257
+ const pair = hostPairFromEntry(entry as Record<string, unknown>);
202
258
  if (pair) return pair;
203
259
  }
204
260
  return undefined;
@@ -224,6 +280,22 @@ function profileNameForPair(
224
280
  return undefined;
225
281
  }
226
282
 
283
+ function pairMatchesProfile(
284
+ capabilityState: CognitiveRoutingCapabilityState,
285
+ pair: CognitiveRoutingPair,
286
+ profileName: CognitiveRoutingProfileName,
287
+ ): boolean {
288
+ const profile = capabilityState.resolvedProfiles[profileName];
289
+ return Boolean(
290
+ profile &&
291
+ pairEquals(pair, {
292
+ provider: profile.provider,
293
+ modelId: profile.model,
294
+ thinkingLevel: profile.effectiveThinkingLevel,
295
+ }),
296
+ );
297
+ }
298
+
227
299
  function asIntent(value: unknown): CognitiveRoutingIntent | undefined {
228
300
  if (!value || typeof value !== "object") return undefined;
229
301
  const candidate = value as Record<string, unknown>;
@@ -252,6 +324,9 @@ function asIntent(value: unknown): CognitiveRoutingIntent | undefined {
252
324
  : {}),
253
325
  ...(fromPair ? { fromPair } : {}),
254
326
  ...(isProfileName(candidate.fromProfile) ? { fromProfile: candidate.fromProfile } : {}),
327
+ ...(candidate.fromControl === "automatic" || candidate.fromControl === "manual"
328
+ ? { fromControl: candidate.fromControl }
329
+ : {}),
255
330
  ...(typeof candidate.reason === "string" ? { reason: candidate.reason } : {}),
256
331
  ...(typeof candidate.branchId === "string" || candidate.branchId === null
257
332
  ? { branchId: candidate.branchId as string | null }
@@ -269,6 +344,44 @@ function asIntent(value: unknown): CognitiveRoutingIntent | undefined {
269
344
  };
270
345
  }
271
346
 
347
+ function asInactiveEntry(value: unknown): CognitiveRoutingInactiveEntry | undefined {
348
+ if (!value || typeof value !== "object") return undefined;
349
+ const candidate = value as Record<string, unknown>;
350
+ const pair = asPair(candidate.pair);
351
+ if (
352
+ candidate.version !== 1 ||
353
+ candidate.source !== "user" ||
354
+ typeof candidate.reason !== "string" ||
355
+ candidate.reason.length === 0 ||
356
+ !pair
357
+ ) {
358
+ return undefined;
359
+ }
360
+ return {
361
+ version: 1,
362
+ source: "user",
363
+ reason: candidate.reason,
364
+ ...(typeof candidate.branchId === "string" || candidate.branchId === null
365
+ ? { branchId: candidate.branchId as string | null }
366
+ : {}),
367
+ pair,
368
+ };
369
+ }
370
+
371
+ function latestInactiveEntry(
372
+ entries: readonly unknown[],
373
+ ): { entry: CognitiveRoutingInactiveEntry; position: number } | undefined {
374
+ for (let index = entries.length - 1; index >= 0; index -= 1) {
375
+ const candidate = entries[index];
376
+ if (!candidate || typeof candidate !== "object") continue;
377
+ const record = candidate as Record<string, unknown>;
378
+ if (record.type !== "custom" || record.customType !== COGNITIVE_ROUTING_INACTIVE_ENTRY) continue;
379
+ const entry = asInactiveEntry(record.data);
380
+ if (entry) return { entry, position: index };
381
+ }
382
+ return undefined;
383
+ }
384
+
272
385
  function entriesFrom(context: ControllerContext): readonly unknown[] {
273
386
  try {
274
387
  const entries = context.sessionManager?.getEntries?.();
@@ -338,7 +451,14 @@ function latestControlMode(entries: readonly unknown[], intent: CognitiveRouting
338
451
  }
339
452
  if (candidate.customType !== COGNITIVE_ROUTING_INTENT_ENTRY) continue;
340
453
  const candidateIntent = asIntent(candidate.data);
341
- if (!candidateIntent || candidateIntent.phase !== "prepared" || candidateIntent.epoch !== intent.epoch) continue;
454
+ if (!candidateIntent || candidateIntent.epoch !== intent.epoch) continue;
455
+ if (candidateIntent.phase === "abandoned") {
456
+ if (candidateIntent.fromControl === "manual" && candidateIntent.fromProfile) {
457
+ return `manual-${candidateIntent.fromProfile}` as CognitiveRoutingControlMode;
458
+ }
459
+ if (candidateIntent.fromControl === "automatic") return "automatic";
460
+ continue;
461
+ }
342
462
  if (candidateIntent.kind === "profile" || candidateIntent.kind === "activation") {
343
463
  return controlModeForIntent(candidateIntent);
344
464
  }
@@ -346,14 +466,86 @@ function latestControlMode(entries: readonly unknown[], intent: CognitiveRouting
346
466
  return "automatic";
347
467
  }
348
468
 
469
+ function intentEntryIndex(entries: readonly unknown[], intent: CognitiveRoutingIntent): number {
470
+ for (let index = 0; index < entries.length; index += 1) {
471
+ const entry = entries[index];
472
+ if (!entry || typeof entry !== "object") continue;
473
+ const candidate = entry as Record<string, unknown>;
474
+ if (candidate.type !== "custom" || candidate.customType !== COGNITIVE_ROUTING_INTENT_ENTRY) continue;
475
+ const parsed = asIntent(candidate.data);
476
+ if (parsed?.correlationId === intent.correlationId && parsed.phase === "prepared") return index;
477
+ }
478
+ return entries.length;
479
+ }
480
+
481
+ function matchesBaseline(candidate: CognitiveRoutingIntent, failed: CognitiveRoutingIntent): boolean {
482
+ if (failed.fromPair && !pairEquals(candidate.target, failed.fromPair)) return false;
483
+ if (failed.fromProfile && candidate.profile !== failed.fromProfile) return false;
484
+ return true;
485
+ }
486
+
487
+ export function resolveControlBeforeIntent(
488
+ entries: readonly unknown[],
489
+ intent: CognitiveRoutingIntent,
490
+ ): CognitiveRoutingControlMode | undefined {
491
+ if (intent.fromControl === "automatic") return "automatic";
492
+ if (intent.fromControl === "manual") {
493
+ return intent.fromProfile ? (`manual-${intent.fromProfile}` as CognitiveRoutingControlMode) : undefined;
494
+ }
495
+
496
+ const priorEntries = entries.slice(0, intentEntryIndex(entries, intent));
497
+ const abandonedCorrelations = new Set<string>();
498
+ for (const entry of priorEntries) {
499
+ if (!entry || typeof entry !== "object") continue;
500
+ const candidate = entry as Record<string, unknown>;
501
+ if (candidate.type !== "custom" || candidate.customType !== COGNITIVE_ROUTING_INTENT_ENTRY) continue;
502
+ const parsed = asIntent(candidate.data);
503
+ if (parsed?.phase === "abandoned") abandonedCorrelations.add(parsed.correlationId);
504
+ }
505
+
506
+ let resolved: CognitiveRoutingControlMode | undefined;
507
+ for (const entry of priorEntries) {
508
+ if (!entry || typeof entry !== "object") continue;
509
+ const candidate = entry as Record<string, unknown>;
510
+ if (candidate.type !== "custom") continue;
511
+ if (candidate.customType === COGNITIVE_ROUTING_CONTROL_ENTRY) {
512
+ const data = candidate.data;
513
+ if (
514
+ data &&
515
+ typeof data === "object" &&
516
+ ((data as Record<string, unknown>).version === 1 || (data as Record<string, unknown>).version === 2) &&
517
+ (data as Record<string, unknown>).epoch === intent.epoch &&
518
+ (data as Record<string, unknown>).control === "automatic"
519
+ ) {
520
+ resolved = "automatic";
521
+ }
522
+ continue;
523
+ }
524
+ if (candidate.customType !== COGNITIVE_ROUTING_INTENT_ENTRY) continue;
525
+ const parsed = asIntent(candidate.data);
526
+ if (
527
+ !parsed ||
528
+ parsed.phase !== "prepared" ||
529
+ parsed.epoch !== intent.epoch ||
530
+ abandonedCorrelations.has(parsed.correlationId) ||
531
+ (parsed.kind !== "profile" && parsed.kind !== "activation") ||
532
+ !hasMatchingHostEntry(priorEntries, parsed) ||
533
+ !matchesBaseline(parsed, intent)
534
+ ) {
535
+ continue;
536
+ }
537
+ resolved = controlModeForIntent(parsed);
538
+ }
539
+ return resolved;
540
+ }
541
+
349
542
  function hasMatchingHostEntry(entries: readonly unknown[], intent: CognitiveRoutingIntent): boolean {
350
543
  return entries.some((entry) => {
351
544
  if (!entry || typeof entry !== "object") return false;
352
545
  const candidate = entry as Record<string, unknown>;
353
546
  return (
354
- candidate.type === "model_state_change" &&
355
- candidate.correlationId === intent.correlationId &&
356
- pairEquals(asPair(candidate), intent.target)
547
+ hostCorrelationIdFromEntry(candidate) === intent.correlationId &&
548
+ pairEquals(hostPairFromEntry(candidate), intent.target)
357
549
  );
358
550
  });
359
551
  }
@@ -362,13 +554,18 @@ export class CognitiveRoutingController {
362
554
  private readonly capabilityState: CognitiveRoutingCapabilityState;
363
555
  private readonly pi: ControllerPi;
364
556
  private readonly ctx: ControllerContext;
557
+ private readonly initialSessionStart: CognitiveRoutingSessionStart | undefined;
365
558
  private readonly label: string;
366
559
  private readonly idFactory: () => string;
560
+ private readonly liveStateAuthoritative: boolean;
367
561
  private lease: ModelStateLease | undefined;
368
562
  private activeProfile: CognitiveRoutingProfileName | undefined;
369
563
  private epoch: string | undefined;
370
564
  private returnTarget: CognitiveRoutingPair | undefined;
371
565
  private controlMode: CognitiveRoutingControlMode = "automatic";
566
+ private nativeInactiveReason: string | undefined;
567
+ private nativeBlockedReason: string | undefined;
568
+ private transitionInFlight = false;
372
569
  private branchPending = false;
373
570
  private operation: Promise<unknown> = Promise.resolve();
374
571
 
@@ -376,18 +573,32 @@ export class CognitiveRoutingController {
376
573
  this.capabilityState = options.capabilityState;
377
574
  this.pi = options.pi;
378
575
  this.ctx = options.ctx;
576
+ this.initialSessionStart = options.initialSessionStart;
379
577
  this.label = options.label ?? "Cognitive Routing";
380
578
  this.idFactory = options.idFactory ?? randomUUID;
579
+ this.liveStateAuthoritative = options.liveStateAuthoritative === true;
381
580
  }
382
581
 
383
582
  state(): CognitiveRoutingControllerState {
384
- const hostPair = latestHostPair(this.ctx);
385
- const observedPair = hostPair ?? this.currentPair();
583
+ const hostPair = this.liveStateAuthoritative ? undefined : latestHostPair(this.ctx);
584
+ const observedPair = this.liveStateAuthoritative ? this.currentPair() : (hostPair ?? this.currentPair());
386
585
  const observedProfile = this.activeProfile ? profileNameForPair(this.capabilityState, observedPair) : undefined;
387
- const activeProfile = hostPair ? observedProfile : (observedProfile ?? this.activeProfile);
586
+ const activeProfile = this.liveStateAuthoritative
587
+ ? observedProfile
588
+ : hostPair
589
+ ? observedProfile
590
+ : (observedProfile ?? this.activeProfile);
591
+ const effective = this.lease !== undefined && activeProfile !== undefined && !this.branchPending;
388
592
  return {
389
- effective: this.lease !== undefined && activeProfile !== undefined && !this.branchPending,
593
+ effective,
390
594
  controlMode: this.controlMode,
595
+ ...(this.nativeBlockedReason
596
+ ? { runtimeStatus: "blocked" as const, runtimeReason: this.nativeBlockedReason }
597
+ : this.nativeInactiveReason
598
+ ? { runtimeStatus: "inactive" as const, runtimeReason: this.nativeInactiveReason }
599
+ : effective
600
+ ? { runtimeStatus: "active" as const }
601
+ : {}),
391
602
  ...(activeProfile ? { activeProfile } : {}),
392
603
  ...(this.epoch ? { epoch: this.epoch } : {}),
393
604
  ...(this.returnTarget ? { returnTarget: { ...this.returnTarget } } : {}),
@@ -449,13 +660,86 @@ export class CognitiveRoutingController {
449
660
  return this.enqueue(() => this._reconcileBranch(mechanism));
450
661
  }
451
662
 
452
- private async _activate(): Promise<CognitiveRoutingActivationResult> {
663
+ async observeNativeChange(
664
+ reason: string,
665
+ source: "set" | "cycle" | "restore" | undefined = undefined,
666
+ ): Promise<CognitiveRoutingNativeChangeResult> {
667
+ if (this.transitionInFlight || source === "restore") return { status: "ignored" };
668
+ return this.enqueue(() => this._observeNativeChange(reason, source));
669
+ }
670
+
671
+ async reconcileLiveState(reason = "Pi model state diverged"): Promise<CognitiveRoutingNativeChangeResult> {
672
+ if (this.transitionInFlight) return { status: "ignored" };
673
+ return this.enqueue(() => this._observeNativeChange(reason));
674
+ }
675
+
676
+ private recordNativeInactive(reason: string, pair: CognitiveRoutingPair): CognitiveRoutingNativeChangeResult {
677
+ try {
678
+ this.pi.appendEntryDurable(COGNITIVE_ROUTING_INACTIVE_ENTRY, {
679
+ version: 1,
680
+ source: "user",
681
+ reason,
682
+ branchId: this.currentBranchId(),
683
+ pair,
684
+ });
685
+ } catch {
686
+ this.nativeInactiveReason = undefined;
687
+ this.nativeBlockedReason = "inactive_state_persistence_failed";
688
+ return { status: "blocked", reason: this.nativeBlockedReason };
689
+ }
690
+
691
+ this.nativeInactiveReason = reason;
692
+ this.nativeBlockedReason = undefined;
693
+ return { status: "inactive", reason };
694
+ }
695
+
696
+ private async _observeNativeChange(
697
+ reason: string,
698
+ source: "set" | "cycle" | "restore" | undefined = undefined,
699
+ ): Promise<CognitiveRoutingNativeChangeResult> {
700
+ if (!this.lease || !this.activeProfile) return { status: "ignored" };
701
+
702
+ const profile = this.capabilityState.resolvedProfiles[this.activeProfile];
703
+ const current = this.currentPair();
704
+ const expected = profile
705
+ ? {
706
+ provider: profile.provider,
707
+ modelId: profile.model,
708
+ thinkingLevel: profile.effectiveThinkingLevel,
709
+ }
710
+ : undefined;
711
+ if (current && expected && pairEquals(current, expected) && source !== "set") return { status: "ignored" };
712
+ const lease = this.lease;
713
+ if (!current) {
714
+ await this.releaseLease(lease);
715
+ this.clearActiveState();
716
+ this.nativeBlockedReason = "native_state_unavailable";
717
+ return { status: "blocked", reason: this.nativeBlockedReason };
718
+ }
719
+
720
+ const result = this.recordNativeInactive(reason, current);
721
+ await this.releaseLease(lease);
722
+ this.clearActiveState();
723
+ return result;
724
+ }
725
+
726
+ private async _activate(
727
+ allowInactive = false,
728
+ requestedProfile?: CognitiveRoutingProfileName,
729
+ requestedControlMode?: CognitiveRoutingControlMode,
730
+ ): Promise<CognitiveRoutingActivationResult> {
453
731
  if (this.state().effective && this.activeProfile) {
454
732
  return { status: "active", profile: this.activeProfile };
455
733
  }
456
734
  if (!this.capabilityState.effective) {
457
735
  return { status: "inactive", reason: "not_effective" };
458
736
  }
737
+ if ((this.nativeInactiveReason || this.nativeBlockedReason) && !allowInactive) {
738
+ return {
739
+ status: "inactive",
740
+ reason: this.nativeBlockedReason ? "runtime_blocked" : "native_override",
741
+ };
742
+ }
459
743
 
460
744
  const sessionEntries = entriesFrom(this.ctx);
461
745
  const lifecycleIntent = latestIntent(
@@ -465,7 +749,10 @@ export class CognitiveRoutingController {
465
749
  if (lifecycleIntent && !hasMatchingHostEntry(sessionEntries, lifecycleIntent)) {
466
750
  return { status: "inactive", reason: "pending_intent" };
467
751
  }
468
- if (lifecycleIntent?.kind === "activation") {
752
+ if (
753
+ lifecycleIntent?.kind === "activation" &&
754
+ !(allowInactive && (this.nativeInactiveReason || this.nativeBlockedReason))
755
+ ) {
469
756
  return { status: "inactive", reason: "recover_required" };
470
757
  }
471
758
  const preservedReload = lifecycleIntent?.kind === "closing" && lifecycleIntent.resumeProfile !== undefined;
@@ -478,19 +765,29 @@ export class CognitiveRoutingController {
478
765
  }
479
766
  const epoch = this.epoch ?? this.idFactory();
480
767
  const branchId = this.currentBranchId();
481
- const sessionStart = this.capabilityState.sessionStart ?? {
482
- control: "automatic" as const,
483
- profile: "standard" as const,
484
- };
485
- const profileName: CognitiveRoutingProfileName = preservedReload
486
- ? lifecycleIntent.resumeProfile
487
- : sessionStart.profile;
768
+ const sessionStart = this.initialSessionStart ??
769
+ this.capabilityState.sessionStart ?? {
770
+ control: "automatic" as const,
771
+ profile: "reasoning" as const,
772
+ };
773
+ let profileName: CognitiveRoutingProfileName;
774
+ if (requestedProfile) {
775
+ profileName = requestedProfile;
776
+ } else if (preservedReload) {
777
+ profileName = lifecycleIntent.resumeProfile;
778
+ } else if (sessionStart.control === "automatic") {
779
+ profileName = "reasoning";
780
+ } else {
781
+ profileName = sessionStart.profile;
782
+ }
488
783
  const profile = this.capabilityState.resolvedProfiles[profileName];
489
784
  if (!profile) {
490
785
  return { status: "inactive", reason: preservedReload ? "resume_profile_unavailable" : "profile_unavailable" };
491
786
  }
492
787
  let controlMode: CognitiveRoutingControlMode;
493
- if (preservedReload) {
788
+ if (requestedControlMode) {
789
+ controlMode = requestedControlMode;
790
+ } else if (preservedReload) {
494
791
  controlMode = lifecycleIntent.resumeControl === "manual" ? `manual-${profileName}` : "automatic";
495
792
  } else {
496
793
  controlMode = sessionStart.control === "manual" ? `manual-${profileName}` : "automatic";
@@ -547,7 +844,7 @@ export class CognitiveRoutingController {
547
844
  (candidate) => candidate.kind === "profile" && candidate.epoch === this.epoch,
548
845
  )
549
846
  : undefined;
550
- const fromPair = latestHostPair(this.ctx) ?? this.currentPair();
847
+ const fromPair = this.observedHostPair();
551
848
  const fromProfile = this.activeProfile ?? profileNameForPair(this.capabilityState, fromPair);
552
849
  const intent = this.prepareIntent({
553
850
  kind: "closing",
@@ -575,7 +872,7 @@ export class CognitiveRoutingController {
575
872
 
576
873
  this.clearActiveState(false);
577
874
  try {
578
- const result = await lease.setState({ ...returnTarget, correlationId: intent.correlationId });
875
+ const result = await this.setState(lease, { ...returnTarget, correlationId: intent.correlationId });
579
876
  if (result.status !== "applied" && result.status !== "unchanged") {
580
877
  return { status: "inactive", reason: "restore_rejected" };
581
878
  }
@@ -587,9 +884,133 @@ export class CognitiveRoutingController {
587
884
  }
588
885
  }
589
886
 
887
+ private async recoverTerminalIntent(
888
+ intent: CognitiveRoutingIntent,
889
+ ): Promise<CognitiveRoutingRecoveryResult | undefined> {
890
+ const commit = latestPiSessionCommit(branchEntriesFrom(this.ctx), intent.correlationId);
891
+ if (!commit) return undefined;
892
+ if (commit.phase === "prepared") {
893
+ if (!this.pi.recoverPreparedModelState) {
894
+ return { status: "pending", reason: "prepared_recovery_unsupported" };
895
+ }
896
+ let recovery: { status: "restored" | "rollback-failed" | "rejected" };
897
+ try {
898
+ recovery = await this.pi.recoverPreparedModelState({
899
+ fromPair: commit.fromPair,
900
+ target: commit.target,
901
+ correlationId: commit.correlationId,
902
+ });
903
+ } catch {
904
+ return { status: "pending", reason: "prepared_recovery_failed" };
905
+ }
906
+ if (recovery.status === "rollback-failed") {
907
+ return { status: "pending", reason: "rollback_failed" };
908
+ }
909
+ if (recovery.status !== "restored") {
910
+ return { status: "pending", reason: "prepared_recovery_failed" };
911
+ }
912
+ try {
913
+ this.abandonPreparedIntent(intent, "Pi session transition interrupted.");
914
+ } catch {
915
+ return { status: "pending", reason: "intent_abandon_failed" };
916
+ }
917
+ const recovered = await this._recover();
918
+ return recovered.status === "inactive" && recovered.reason === "no_pending_intent"
919
+ ? { status: "inactive", reason: "intent_abandoned" }
920
+ : recovered;
921
+ }
922
+ if (commit.phase !== "aborted") return undefined;
923
+ if (commit.status === "rollback-failed") {
924
+ return { status: "pending", reason: "rollback_failed" };
925
+ }
926
+
927
+ try {
928
+ this.abandonPreparedIntent(intent, "Pi session transition rolled back.");
929
+ } catch {
930
+ return { status: "pending", reason: "intent_abandon_failed" };
931
+ }
932
+ return this._recover();
933
+ }
934
+
935
+ private async recoverTerminalProfileIntent(
936
+ intent: CognitiveRoutingIntent,
937
+ ): Promise<CognitiveRoutingRecoveryResult | undefined> {
938
+ const commit = latestPiSessionCommit(branchEntriesFrom(this.ctx), intent.correlationId);
939
+ if (!commit || commit.phase === "committed") return undefined;
940
+ if (commit.phase === "aborted" && commit.status === "rollback-failed") {
941
+ return { status: "pending", reason: "rollback_failed" };
942
+ }
943
+
944
+ const controlMode = resolveControlBeforeIntent(branchEntriesFrom(this.ctx), intent);
945
+ if (
946
+ !controlMode ||
947
+ !intent.fromPair ||
948
+ !intent.fromProfile ||
949
+ !pairMatchesProfile(this.capabilityState, intent.fromPair, intent.fromProfile)
950
+ ) {
951
+ return { status: "pending", reason: "baseline_control_unknown" };
952
+ }
953
+
954
+ if (commit.phase === "prepared") {
955
+ if (!this.pi.recoverPreparedModelState) {
956
+ return { status: "pending", reason: "prepared_recovery_unsupported" };
957
+ }
958
+ let recovery: { status: "restored" | "rollback-failed" | "rejected" };
959
+ try {
960
+ recovery = await this.pi.recoverPreparedModelState({
961
+ fromPair: commit.fromPair,
962
+ target: commit.target,
963
+ correlationId: commit.correlationId,
964
+ });
965
+ } catch {
966
+ return { status: "pending", reason: "prepared_recovery_failed" };
967
+ }
968
+ if (recovery.status === "rollback-failed") {
969
+ return { status: "pending", reason: "rollback_failed" };
970
+ }
971
+ if (recovery.status !== "restored") {
972
+ return { status: "pending", reason: "prepared_recovery_failed" };
973
+ }
974
+ }
975
+
976
+ try {
977
+ this.abandonPreparedIntent(intent, "Pi session transition interrupted or rolled back.");
978
+ } catch {
979
+ return { status: "pending", reason: "intent_abandon_failed" };
980
+ }
981
+
982
+ const recovered = await this.activatePrepared({
983
+ kind: "profile",
984
+ controlMode,
985
+ source: "system",
986
+ mechanism: "recovery",
987
+ ...(decisionCorrelationForIntent(intent) ? { decisionCorrelationId: decisionCorrelationForIntent(intent) } : {}),
988
+ branchId: this.currentBranchId(),
989
+ epoch: intent.epoch,
990
+ returnTarget: intent.returnTarget,
991
+ target: intent.fromPair,
992
+ profile: intent.fromProfile,
993
+ });
994
+ return recovered.status === "active" ? recovered : { status: "pending", reason: "baseline_recovery_failed" };
995
+ }
996
+
590
997
  private async _recover(): Promise<CognitiveRoutingRecoveryResult> {
591
998
  const sessionEntries = entriesFrom(this.ctx);
592
999
  const branchEntries = branchEntriesFrom(this.ctx);
1000
+ const inactive = latestInactiveEntry(branchEntries);
1001
+ const branchLifecycleIntent = latestIntent(
1002
+ branchEntries,
1003
+ (intent) => intent.kind === "activation" || intent.kind === "closing",
1004
+ );
1005
+ if (
1006
+ inactive &&
1007
+ (!branchLifecycleIntent || intentEntryIndex(branchEntries, branchLifecycleIntent) <= inactive.position)
1008
+ ) {
1009
+ this.nativeInactiveReason = inactive.entry.reason;
1010
+ this.nativeBlockedReason = undefined;
1011
+ this.clearActiveState();
1012
+ return { status: "inactive", reason: "native_override" };
1013
+ }
593
1014
  const lifecycleIntent = latestIntent(
594
1015
  sessionEntries,
595
1016
  (intent) => intent.kind === "activation" || intent.kind === "closing",
@@ -597,6 +1018,10 @@ export class CognitiveRoutingController {
597
1018
  if (!lifecycleIntent) {
598
1019
  return { status: "inactive", reason: "no_pending_intent" };
599
1020
  }
1021
+ if (lifecycleIntent.kind === "activation") {
1022
+ const lifecycleTerminal = await this.recoverTerminalIntent(lifecycleIntent);
1023
+ if (lifecycleTerminal) return lifecycleTerminal;
1024
+ }
600
1025
  if (lifecycleIntent.kind === "closing") {
601
1026
  return hasMatchingHostEntry(sessionEntries, lifecycleIntent)
602
1027
  ? { status: "inactive", reason: "closed" }
@@ -618,6 +1043,19 @@ export class CognitiveRoutingController {
618
1043
  thinkingLevel: currentActivationProfile.effectiveThinkingLevel,
619
1044
  };
620
1045
  const activationMatched = hasMatchingHostEntry(sessionEntries, lifecycleIntent);
1046
+ if (this.liveStateAuthoritative && activationMatched) {
1047
+ const livePair = this.currentPair();
1048
+ if (!livePair) {
1049
+ this.clearActiveState();
1050
+ this.nativeBlockedReason = "native_state_unavailable";
1051
+ return { status: "inactive", reason: "native_override" };
1052
+ }
1053
+ if (!pairEquals(livePair, activationTarget)) {
1054
+ this.recordNativeInactive("Pi model state diverged during recovery", livePair);
1055
+ this.clearActiveState();
1056
+ return { status: "inactive", reason: "native_override" };
1057
+ }
1058
+ }
621
1059
  if (activationMatched && !pairEquals(activationTarget, lifecycleIntent.target)) {
622
1060
  return { status: "pending", reason: "applied_target_changed" };
623
1061
  }
@@ -627,7 +1065,7 @@ export class CognitiveRoutingController {
627
1065
  if (!activationMatched) {
628
1066
  const retried = await this.activatePrepared({
629
1067
  kind: "activation",
630
- controlMode: "automatic",
1068
+ controlMode: latestControlMode(branchEntries, lifecycleIntent),
631
1069
  source: "system",
632
1070
  mechanism: "recovery",
633
1071
  branchId: this.currentBranchId(),
@@ -644,6 +1082,8 @@ export class CognitiveRoutingController {
644
1082
  (intent) => intent.kind === "profile" && intent.epoch === lifecycleIntent.epoch,
645
1083
  );
646
1084
  if (profileIntent) {
1085
+ const terminal = await this.recoverTerminalProfileIntent(profileIntent);
1086
+ if (terminal) return terminal;
647
1087
  const currentProfile = this.capabilityState.resolvedProfiles[profileIntent.profile ?? "standard"];
648
1088
  if (!currentProfile) {
649
1089
  return this.abandonIntent(profileIntent, "Prepared profile is unavailable.");
@@ -678,7 +1118,7 @@ export class CognitiveRoutingController {
678
1118
 
679
1119
  return this.activatePrepared({
680
1120
  kind: "activation",
681
- controlMode: "automatic",
1121
+ controlMode: latestControlMode(branchEntries, lifecycleIntent),
682
1122
  source: "system",
683
1123
  mechanism: "recovery",
684
1124
  branchId: this.currentBranchId(),
@@ -690,7 +1130,7 @@ export class CognitiveRoutingController {
690
1130
  }
691
1131
 
692
1132
  private async recoverClosingIntent(intent: CognitiveRoutingIntent): Promise<CognitiveRoutingRecoveryResult> {
693
- const fromPair = latestHostPair(this.ctx) ?? this.currentPair();
1133
+ const fromPair = this.observedHostPair();
694
1134
  const fromProfile = intent.fromProfile ?? profileNameForPair(this.capabilityState, fromPair);
695
1135
  const retryIntent = this.prepareIntent({
696
1136
  kind: "closing",
@@ -726,7 +1166,10 @@ export class CognitiveRoutingController {
726
1166
  }
727
1167
 
728
1168
  try {
729
- const result = await acquisition.lease.setState({ ...intent.target, correlationId: retryIntent.correlationId });
1169
+ const result = await this.setState(acquisition.lease, {
1170
+ ...intent.target,
1171
+ correlationId: retryIntent.correlationId,
1172
+ });
730
1173
  return result.status === "applied" || result.status === "unchanged"
731
1174
  ? { status: "inactive", reason: "restored" }
732
1175
  : { status: "pending", reason: "closing_recovery_rejected" };
@@ -737,20 +1180,24 @@ export class CognitiveRoutingController {
737
1180
  }
738
1181
  }
739
1182
 
1183
+ private abandonPreparedIntent(intent: CognitiveRoutingIntent, reason: string): void {
1184
+ const fromPair = intent.fromPair ?? this.observedHostPair();
1185
+ const fromProfile = intent.fromProfile ?? profileNameForPair(this.capabilityState, fromPair);
1186
+ this.pi.appendEntryDurable(COGNITIVE_ROUTING_INTENT_ENTRY, {
1187
+ ...intent,
1188
+ version: 2,
1189
+ phase: "abandoned",
1190
+ source: "system",
1191
+ mechanism: intent.mechanism ?? "recovery",
1192
+ ...(fromPair ? { fromPair } : {}),
1193
+ ...(fromProfile ? { fromProfile } : {}),
1194
+ reason,
1195
+ });
1196
+ }
1197
+
740
1198
  private async abandonIntent(intent: CognitiveRoutingIntent, reason: string): Promise<CognitiveRoutingRecoveryResult> {
741
1199
  try {
742
- const fromPair = intent.fromPair ?? latestHostPair(this.ctx) ?? this.currentPair();
743
- const fromProfile = intent.fromProfile ?? profileNameForPair(this.capabilityState, fromPair);
744
- this.pi.appendEntryDurable(COGNITIVE_ROUTING_INTENT_ENTRY, {
745
- ...intent,
746
- version: 2,
747
- phase: "abandoned",
748
- source: "system",
749
- mechanism: intent.mechanism ?? "recovery",
750
- ...(fromPair ? { fromPair } : {}),
751
- ...(fromProfile ? { fromProfile } : {}),
752
- reason,
753
- });
1200
+ this.abandonPreparedIntent(intent, reason);
754
1201
  const recovered = await this._recover();
755
1202
  return recovered.status === "inactive" && recovered.reason === "no_pending_intent"
756
1203
  ? { status: "inactive", reason: "intent_abandoned" }
@@ -853,8 +1300,9 @@ export class CognitiveRoutingController {
853
1300
  return { status: "inactive", reason: "not_effective" };
854
1301
  }
855
1302
 
856
- const activation = this.state().effective ? { status: "active" as const } : await this._activate();
857
- if (activation.status !== "active") return activation;
1303
+ if (!this.state().effective) {
1304
+ return this._activate(true, profile, `manual-${profile}`);
1305
+ }
858
1306
  return this._setProfile(profile, {
859
1307
  controlMode: `manual-${profile}`,
860
1308
  source: "user",
@@ -902,7 +1350,7 @@ export class CognitiveRoutingController {
902
1350
  modelId: resolvedProfile.model,
903
1351
  thinkingLevel: resolvedProfile.effectiveThinkingLevel,
904
1352
  };
905
- const fromPair = latestHostPair(this.ctx) ?? this.currentPair();
1353
+ const fromPair = this.observedHostPair();
906
1354
  if (!fromPair) return { status: "inactive", reason: "current_state_unavailable" };
907
1355
  const fromProfile = this.activeProfile ?? profileNameForPair(this.capabilityState, fromPair);
908
1356
  const correlationId = this.idFactory();
@@ -917,6 +1365,7 @@ export class CognitiveRoutingController {
917
1365
  ...(decisionCorrelationId ? { decisionCorrelationId } : {}),
918
1366
  ...(fromPair ? { fromPair } : {}),
919
1367
  ...(fromProfile ? { fromProfile } : {}),
1368
+ fromControl: options.controlMode === "automatic" ? "automatic" : "manual",
920
1369
  reason: options.reason,
921
1370
  branchId: this.currentBranchId(),
922
1371
  epoch: this.epoch,
@@ -946,8 +1395,27 @@ export class CognitiveRoutingController {
946
1395
  }
947
1396
 
948
1397
  try {
949
- const result = await lease.setState({ ...target, correlationId: intent.correlationId });
1398
+ const result = await this.setState(lease, { ...target, correlationId: intent.correlationId });
950
1399
  if (result.status !== "applied" && result.status !== "unchanged") {
1400
+ const commit = latestPiSessionCommit(branchEntriesFrom(this.ctx), intent.correlationId);
1401
+ if (result.status === "rejected" && commit?.phase === "aborted" && commit.status === "rollback-failed") {
1402
+ try {
1403
+ this.abandonPreparedIntent(intent, "Pi session rollback failed.");
1404
+ } catch {
1405
+ // Fail closed even when the terminal abandonment cannot be persisted.
1406
+ }
1407
+ await this.releaseLease(lease);
1408
+ this.clearActiveState();
1409
+ return { status: "inactive", reason: "rollback_failed" };
1410
+ }
1411
+ if (result.status === "rejected" && commit?.phase === "aborted" && commit.status === "rolled-back") {
1412
+ try {
1413
+ this.abandonPreparedIntent(intent, "Pi session transition rolled back.");
1414
+ this.branchPending = false;
1415
+ } catch {
1416
+ // Preserve the pending boundary when the terminal abandonment cannot be persisted.
1417
+ }
1418
+ }
951
1419
  return { status: "inactive", reason: "transition_rejected" };
952
1420
  }
953
1421
  this.activeProfile = profile;
@@ -961,8 +1429,27 @@ export class CognitiveRoutingController {
961
1429
 
962
1430
  private async _setAutomaticControl(mechanism: CognitiveRoutingMechanism): Promise<CognitiveRoutingAutomaticResult> {
963
1431
  if (!this.lease || !this.epoch) {
964
- return { status: "rejected", reason: "not_active" };
1432
+ if (!this.nativeInactiveReason && !this.nativeBlockedReason) return { status: "rejected", reason: "not_active" };
1433
+ const activation = await this._activate(true, this.profileForCurrentPair(), "automatic");
1434
+ return activation.status === "active"
1435
+ ? { status: "automatic" }
1436
+ : { status: "rejected", reason: activation.reason };
965
1437
  }
1438
+ const current = this.state();
1439
+ if (!current.activeProfile) {
1440
+ return { status: "rejected", reason: "profile_unknown" };
1441
+ }
1442
+ if (current.activeProfile !== "reasoning") {
1443
+ const transition = await this._setProfile("reasoning", {
1444
+ controlMode: "automatic",
1445
+ source: "user",
1446
+ mechanism,
1447
+ reason: "automatic control selects the Reasoning profile",
1448
+ });
1449
+ if (transition.status !== "active") return { status: "rejected", reason: transition.reason };
1450
+ return { status: "automatic" };
1451
+ }
1452
+ if (this.controlMode === "automatic") return { status: "automatic" };
966
1453
  try {
967
1454
  this.pi.appendEntryDurable(COGNITIVE_ROUTING_CONTROL_ENTRY, {
968
1455
  version: 2,
@@ -994,6 +1481,15 @@ export class CognitiveRoutingController {
994
1481
  }
995
1482
  }
996
1483
 
1484
+ private observedHostPair(): CognitiveRoutingPair | undefined {
1485
+ const livePair = this.currentPair();
1486
+ return this.liveStateAuthoritative ? livePair : (latestHostPair(this.ctx) ?? livePair);
1487
+ }
1488
+
1489
+ private profileForCurrentPair(): CognitiveRoutingProfileName | undefined {
1490
+ return profileNameForPair(this.capabilityState, this.currentPair());
1491
+ }
1492
+
997
1493
  private currentPair(): CognitiveRoutingPair | undefined {
998
1494
  const model = this.ctx.model;
999
1495
  if (
@@ -1028,7 +1524,7 @@ export class CognitiveRoutingController {
1028
1524
  target: CognitiveRoutingPair;
1029
1525
  profile: CognitiveRoutingProfileName;
1030
1526
  }): Promise<CognitiveRoutingActivationResult> {
1031
- const fromPair = latestHostPair(this.ctx) ?? this.currentPair();
1527
+ const fromPair = this.observedHostPair();
1032
1528
  if (!fromPair) return { status: "inactive", reason: "current_state_unavailable" };
1033
1529
  const fromProfile = profileNameForPair(this.capabilityState, fromPair);
1034
1530
  const correlationId = this.idFactory();
@@ -1069,7 +1565,7 @@ export class CognitiveRoutingController {
1069
1565
 
1070
1566
  this.lease = acquisition.lease;
1071
1567
  try {
1072
- const result = await acquisition.lease.setState({ ...options.target, correlationId: intent.correlationId });
1568
+ const result = await this.setState(acquisition.lease, { ...options.target, correlationId: intent.correlationId });
1073
1569
  if (result.status !== "applied" && result.status !== "unchanged") {
1074
1570
  await this.releaseLease(acquisition.lease);
1075
1571
  this.clearActiveState();
@@ -1079,6 +1575,8 @@ export class CognitiveRoutingController {
1079
1575
  this.returnTarget = options.returnTarget;
1080
1576
  this.activeProfile = options.profile;
1081
1577
  this.controlMode = options.controlMode;
1578
+ this.nativeInactiveReason = undefined;
1579
+ this.nativeBlockedReason = undefined;
1082
1580
  return { status: "active", profile: options.profile };
1083
1581
  } catch {
1084
1582
  await this.releaseLease(acquisition.lease);
@@ -1096,6 +1594,18 @@ export class CognitiveRoutingController {
1096
1594
  return next;
1097
1595
  }
1098
1596
 
1597
+ private async setState(
1598
+ lease: ModelStateLease,
1599
+ request: CognitiveRoutingPair & { correlationId: string },
1600
+ ): Promise<{ status: string }> {
1601
+ this.transitionInFlight = true;
1602
+ try {
1603
+ return await lease.setState(request);
1604
+ } finally {
1605
+ this.transitionInFlight = false;
1606
+ }
1607
+ }
1608
+
1099
1609
  private async releaseLease(lease: ModelStateLease): Promise<void> {
1100
1610
  try {
1101
1611
  await lease.release();