@hassangameryt/freeflow 0.6.0 → 0.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (122) hide show
  1. package/.claude-plugin/marketplace.json +1 -1
  2. package/.claude-plugin/plugin.json +2 -2
  3. package/.codex-plugin/plugin.json +4 -5
  4. package/README.md +39 -55
  5. package/capabilities/cognitive-routing/SKILL.md +288 -130
  6. package/command-surface.json +0 -24
  7. package/hooks/freeflow-runtime-context.mjs +42 -477
  8. package/hooks/hooks.json +1 -26
  9. package/hooks/tests/check-runtime-context-hook.sh +108 -319
  10. package/package.json +4 -2
  11. package/pi-extension/dist/cognitive-routing/commands.js +3 -3
  12. package/pi-extension/dist/cognitive-routing/config.js +16 -20
  13. package/pi-extension/dist/cognitive-routing/controller.js +478 -48
  14. package/pi-extension/dist/cognitive-routing/history.js +12 -0
  15. package/pi-extension/dist/cognitive-routing/host.js +74 -12
  16. package/pi-extension/dist/cognitive-routing/pi-routing-session.js +414 -0
  17. package/pi-extension/dist/cognitive-routing/pi-session-control.js +320 -0
  18. package/pi-extension/dist/cognitive-routing/tool.js +1 -0
  19. package/pi-extension/dist/cognitive-routing/types.js +1 -1
  20. package/pi-extension/dist/index.js +236 -219
  21. package/pi-extension/dist/runtime/runtime-context.js +126 -286
  22. package/pi-extension/dist/settings/settings-ui.js +52 -336
  23. package/pi-extension/src/cognitive-routing/commands.ts +15 -5
  24. package/pi-extension/src/cognitive-routing/config.ts +16 -20
  25. package/pi-extension/src/cognitive-routing/controller.ts +561 -51
  26. package/pi-extension/src/cognitive-routing/history.ts +12 -0
  27. package/pi-extension/src/cognitive-routing/host.ts +110 -16
  28. package/pi-extension/src/cognitive-routing/pi-routing-session.ts +538 -0
  29. package/pi-extension/src/cognitive-routing/pi-session-control.ts +444 -0
  30. package/pi-extension/src/cognitive-routing/tool.ts +1 -0
  31. package/pi-extension/src/cognitive-routing/types.ts +1 -1
  32. package/pi-extension/src/index.ts +249 -239
  33. package/pi-extension/src/runtime/runtime-context.ts +129 -305
  34. package/pi-extension/src/settings/settings-ui.ts +53 -371
  35. package/pi-extension/tests/cognitive-routing/commands.test.js +1 -1
  36. package/pi-extension/tests/cognitive-routing/controller.test.js +69 -40
  37. package/pi-extension/tests/cognitive-routing/extension-lifecycle.test.js +128 -57
  38. package/pi-extension/tests/cognitive-routing/pi-normal-lifecycle.test.js +434 -0
  39. package/pi-extension/tests/cognitive-routing/pi-session-control.test.js +174 -0
  40. package/pi-extension/tests/cognitive-routing/pi-session-history.test.js +91 -0
  41. package/pi-extension/tests/cognitive-routing/profile-resolution.test.js +47 -4
  42. package/pi-extension/tests/integration/mode-free-core.test.js +218 -0
  43. package/pi-extension/tests/integration/pi-extension.test.js +430 -2478
  44. package/pi-extension/tests/integration/prompt-architecture.test.js +124 -79
  45. package/pi-extension/tests/integration/unaffected-regressions.test.js +411 -0
  46. package/runtime/prompts/cognitive-routing.md +5 -4
  47. package/runtime/prompts/conversation-history.md +1 -1
  48. package/runtime/prompts/core.md +77 -6
  49. package/runtime/prompts/interaction-contract.md +6 -2
  50. package/skills/action-selection/SKILL.md +93 -44
  51. package/skills/action-selection/references/trajectory-stalls.md +71 -0
  52. package/skills/bypass/SKILL.md +109 -39
  53. package/skills/commit-work/SKILL.md +94 -55
  54. package/skills/commit-work/references/git-state-edges.md +94 -0
  55. package/skills/commit-work/references/simple-push.md +92 -0
  56. package/skills/commit-work/references/staging-decisions.md +79 -46
  57. package/skills/decision-gate/SKILL.md +80 -22
  58. package/skills/design-for-depth/SKILL.md +208 -43
  59. package/skills/design-for-depth/references/design-pressure-signals.md +108 -240
  60. package/skills/design-for-depth/references/interface-design-loop.md +77 -97
  61. package/skills/design-for-depth/references/module-and-dependency-design.md +147 -0
  62. package/skills/design-for-depth/references/software-design-philosophy.md +127 -173
  63. package/skills/design-for-depth/references/state-and-failure-boundaries.md +136 -0
  64. package/skills/diagnose-failure/SKILL.md +198 -52
  65. package/skills/diagnose-failure/references/diagnostic-loop-catalog.md +19 -5
  66. package/skills/diagnose-failure/references/flaky-and-performance-diagnosis.md +13 -5
  67. package/skills/discuss/SKILL.md +118 -76
  68. package/skills/discuss/references/discussion-continuity.md +84 -32
  69. package/skills/evaluate-skill/SKILL.md +166 -52
  70. package/skills/evaluate-skill/references/evaluation-definition-schema.md +2 -2
  71. package/skills/evaluate-skill/references/evaluation-design.md +56 -30
  72. package/skills/evaluate-skill/references/execution-and-evidence.md +1 -1
  73. package/skills/evaluate-skill/references/review-and-revision.md +62 -21
  74. package/skills/execute-work/SKILL.md +98 -59
  75. package/skills/execute-work/references/code-practices.md +4 -4
  76. package/skills/execute-work/references/execute-work-edges.md +42 -22
  77. package/skills/review-artifact/SKILL.md +132 -71
  78. package/skills/review-artifact/references/adjudicate-artifact-review.md +101 -0
  79. package/skills/review-artifact/references/independent-artifact-reviewer-contract.md +52 -41
  80. package/skills/review-work/SKILL.md +115 -72
  81. package/skills/review-work/references/adjudicate-work-review.md +97 -0
  82. package/skills/review-work/references/independent-work-reviewer-contract.md +40 -35
  83. package/skills/review-work/references/security-risk-lens.md +17 -15
  84. package/skills/setup-freeflow/SKILL.md +14 -17
  85. package/skills/setup-freeflow/references/activation-contract.md +24 -30
  86. package/skills/setup-freeflow/references/host-setup.md +6 -7
  87. package/skills/simplify-code/SKILL.md +97 -39
  88. package/skills/simplify-code/references/simplification-patterns.md +1 -1
  89. package/skills/tdd/SKILL.md +108 -41
  90. package/skills/tdd/references/test-design.md +1 -1
  91. package/skills/track-work/SKILL.md +133 -300
  92. package/skills/track-work/references/working-record-format.md +515 -0
  93. package/skills/track-work/scripts/lib/document.mjs +713 -0
  94. package/skills/track-work/scripts/lib/format.mjs +166 -0
  95. package/skills/track-work/scripts/lib/store.mjs +233 -0
  96. package/skills/track-work/scripts/lib/transitions.mjs +790 -0
  97. package/skills/track-work/scripts/lib/views.mjs +27 -430
  98. package/skills/track-work/scripts/working-record.mjs +292 -98
  99. package/skills/verify-work/SKILL.md +46 -20
  100. package/skills/verify-work/references/browser-runtime-evidence.md +1 -1
  101. package/skills/verify-work/references/integration-evidence.md +1 -7
  102. package/skills/verify-work/references/performance-evidence.md +1 -1
  103. package/skills/workflow/SKILL.md +110 -80
  104. package/skills/workflow/references/domain-skill-composition.md +24 -18
  105. package/skills/write-skill/SKILL.md +218 -62
  106. package/skills/write-skill/references/activation-boundaries.md +63 -21
  107. package/skills/write-skill/references/agent-first-instructions.md +61 -27
  108. package/skills/write-skill/references/progressive-disclosure.md +91 -41
  109. package/skills/write-skill/references/skill-dependency-graphs.md +190 -0
  110. package/runtime/prompts/skills.md +0 -65
  111. package/skills/mode-contract/SKILL.md +0 -104
  112. package/skills/track-work/scripts/lib/codec.mjs +0 -570
  113. package/skills/track-work/scripts/lib/commands.mjs +0 -568
  114. package/skills/track-work/scripts/lib/contract.mjs +0 -303
  115. package/skills/track-work/scripts/lib/model.mjs +0 -506
  116. package/skills/track-work/scripts/lib/operations.mjs +0 -965
  117. package/skills/track-work/scripts/lib/record-store.mjs +0 -264
  118. package/skills/track-work/scripts/lib/result.mjs +0 -37
  119. package/skills/track-work/scripts/lib/rewrites.mjs +0 -740
  120. package/skills/track-work/scripts/lib/schema.mjs +0 -277
  121. package/skills/track-work/scripts/lib/source-inventory.mjs +0 -162
  122. package/skills/track-work/scripts/lib/workspace.mjs +0 -134
@@ -1,7 +1,9 @@
1
1
  import { randomUUID } from "node:crypto";
2
2
  import { readCognitiveRoutingHistory } from "./history.js";
3
+ import { PI_SESSION_MODEL_STATE_ENTRY, parsePiSessionModelStateCommit } from "./pi-session-control.js";
3
4
  export const COGNITIVE_ROUTING_INTENT_ENTRY = "freeflow-cognitive-routing-intent";
4
5
  export const COGNITIVE_ROUTING_CONTROL_ENTRY = "freeflow-cognitive-routing-control";
6
+ export const COGNITIVE_ROUTING_INACTIVE_ENTRY = "freeflow-cognitive-routing-inactive";
5
7
  function isProfileName(value) {
6
8
  return value === "standard" || value === "reasoning";
7
9
  }
@@ -42,18 +44,44 @@ function asPair(value) {
42
44
  thinkingLevel: candidate.thinkingLevel,
43
45
  };
44
46
  }
47
+ function hostPairFromEntry(entry) {
48
+ if (entry.type === "model_state_change") {
49
+ return asPair({
50
+ provider: entry.provider,
51
+ modelId: entry.modelId,
52
+ thinkingLevel: entry.thinkingLevel,
53
+ });
54
+ }
55
+ if (entry.type !== "custom" || entry.customType !== PI_SESSION_MODEL_STATE_ENTRY) return undefined;
56
+ const commit = parsePiSessionModelStateCommit(entry.data);
57
+ if (!commit || commit.phase !== "committed" || commit.status !== "applied") return undefined;
58
+ return commit.target;
59
+ }
60
+ function hostCorrelationIdFromEntry(entry) {
61
+ if (entry.type === "model_state_change") {
62
+ return typeof entry.correlationId === "string" ? entry.correlationId : undefined;
63
+ }
64
+ if (entry.type !== "custom" || entry.customType !== PI_SESSION_MODEL_STATE_ENTRY) return undefined;
65
+ const commit = parsePiSessionModelStateCommit(entry.data);
66
+ return commit?.phase === "committed" && commit.status === "applied" ? commit.correlationId : undefined;
67
+ }
68
+ function latestPiSessionCommit(entries, correlationId) {
69
+ for (let index = entries.length - 1; index >= 0; index -= 1) {
70
+ const entry = entries[index];
71
+ if (!entry || typeof entry !== "object") continue;
72
+ const candidate = entry;
73
+ if (candidate.type !== "custom" || candidate.customType !== PI_SESSION_MODEL_STATE_ENTRY) continue;
74
+ const commit = parsePiSessionModelStateCommit(candidate.data);
75
+ if (commit?.correlationId === correlationId) return commit;
76
+ }
77
+ return undefined;
78
+ }
45
79
  function latestHostPair(context) {
46
80
  const entries = branchEntriesFrom(context);
47
81
  for (let index = entries.length - 1; index >= 0; index -= 1) {
48
82
  const entry = entries[index];
49
83
  if (!entry || typeof entry !== "object") continue;
50
- const candidate = entry;
51
- if (candidate.type !== "model_state_change") continue;
52
- const pair = asPair({
53
- provider: candidate.provider,
54
- modelId: candidate.modelId,
55
- thinkingLevel: candidate.thinkingLevel,
56
- });
84
+ const pair = hostPairFromEntry(entry);
57
85
  if (pair) return pair;
58
86
  }
59
87
  return undefined;
@@ -74,6 +102,17 @@ function profileNameForPair(capabilityState, pair) {
74
102
  }
75
103
  return undefined;
76
104
  }
105
+ function pairMatchesProfile(capabilityState, pair, profileName) {
106
+ const profile = capabilityState.resolvedProfiles[profileName];
107
+ return Boolean(
108
+ profile &&
109
+ pairEquals(pair, {
110
+ provider: profile.provider,
111
+ modelId: profile.model,
112
+ thinkingLevel: profile.effectiveThinkingLevel,
113
+ }),
114
+ );
115
+ }
77
116
  function asIntent(value) {
78
117
  if (!value || typeof value !== "object") return undefined;
79
118
  const candidate = value;
@@ -102,6 +141,9 @@ function asIntent(value) {
102
141
  : {}),
103
142
  ...(fromPair ? { fromPair } : {}),
104
143
  ...(isProfileName(candidate.fromProfile) ? { fromProfile: candidate.fromProfile } : {}),
144
+ ...(candidate.fromControl === "automatic" || candidate.fromControl === "manual"
145
+ ? { fromControl: candidate.fromControl }
146
+ : {}),
105
147
  ...(typeof candidate.reason === "string" ? { reason: candidate.reason } : {}),
106
148
  ...(typeof candidate.branchId === "string" || candidate.branchId === null ? { branchId: candidate.branchId } : {}),
107
149
  epoch: candidate.epoch,
@@ -116,6 +158,38 @@ function asIntent(value) {
116
158
  returnTarget,
117
159
  };
118
160
  }
161
+ function asInactiveEntry(value) {
162
+ if (!value || typeof value !== "object") return undefined;
163
+ const candidate = value;
164
+ const pair = asPair(candidate.pair);
165
+ if (
166
+ candidate.version !== 1 ||
167
+ candidate.source !== "user" ||
168
+ typeof candidate.reason !== "string" ||
169
+ candidate.reason.length === 0 ||
170
+ !pair
171
+ ) {
172
+ return undefined;
173
+ }
174
+ return {
175
+ version: 1,
176
+ source: "user",
177
+ reason: candidate.reason,
178
+ ...(typeof candidate.branchId === "string" || candidate.branchId === null ? { branchId: candidate.branchId } : {}),
179
+ pair,
180
+ };
181
+ }
182
+ function latestInactiveEntry(entries) {
183
+ for (let index = entries.length - 1; index >= 0; index -= 1) {
184
+ const candidate = entries[index];
185
+ if (!candidate || typeof candidate !== "object") continue;
186
+ const record = candidate;
187
+ if (record.type !== "custom" || record.customType !== COGNITIVE_ROUTING_INACTIVE_ENTRY) continue;
188
+ const entry = asInactiveEntry(record.data);
189
+ if (entry) return { entry, position: index };
190
+ }
191
+ return undefined;
192
+ }
119
193
  function entriesFrom(context) {
120
194
  try {
121
195
  const entries = context.sessionManager?.getEntries?.();
@@ -177,21 +251,92 @@ function latestControlMode(entries, intent) {
177
251
  }
178
252
  if (candidate.customType !== COGNITIVE_ROUTING_INTENT_ENTRY) continue;
179
253
  const candidateIntent = asIntent(candidate.data);
180
- if (!candidateIntent || candidateIntent.phase !== "prepared" || candidateIntent.epoch !== intent.epoch) continue;
254
+ if (!candidateIntent || candidateIntent.epoch !== intent.epoch) continue;
255
+ if (candidateIntent.phase === "abandoned") {
256
+ if (candidateIntent.fromControl === "manual" && candidateIntent.fromProfile) {
257
+ return `manual-${candidateIntent.fromProfile}`;
258
+ }
259
+ if (candidateIntent.fromControl === "automatic") return "automatic";
260
+ continue;
261
+ }
181
262
  if (candidateIntent.kind === "profile" || candidateIntent.kind === "activation") {
182
263
  return controlModeForIntent(candidateIntent);
183
264
  }
184
265
  }
185
266
  return "automatic";
186
267
  }
268
+ function intentEntryIndex(entries, intent) {
269
+ for (let index = 0; index < entries.length; index += 1) {
270
+ const entry = entries[index];
271
+ if (!entry || typeof entry !== "object") continue;
272
+ const candidate = entry;
273
+ if (candidate.type !== "custom" || candidate.customType !== COGNITIVE_ROUTING_INTENT_ENTRY) continue;
274
+ const parsed = asIntent(candidate.data);
275
+ if (parsed?.correlationId === intent.correlationId && parsed.phase === "prepared") return index;
276
+ }
277
+ return entries.length;
278
+ }
279
+ function matchesBaseline(candidate, failed) {
280
+ if (failed.fromPair && !pairEquals(candidate.target, failed.fromPair)) return false;
281
+ if (failed.fromProfile && candidate.profile !== failed.fromProfile) return false;
282
+ return true;
283
+ }
284
+ export function resolveControlBeforeIntent(entries, intent) {
285
+ if (intent.fromControl === "automatic") return "automatic";
286
+ if (intent.fromControl === "manual") {
287
+ return intent.fromProfile ? `manual-${intent.fromProfile}` : undefined;
288
+ }
289
+ const priorEntries = entries.slice(0, intentEntryIndex(entries, intent));
290
+ const abandonedCorrelations = new Set();
291
+ for (const entry of priorEntries) {
292
+ if (!entry || typeof entry !== "object") continue;
293
+ const candidate = entry;
294
+ if (candidate.type !== "custom" || candidate.customType !== COGNITIVE_ROUTING_INTENT_ENTRY) continue;
295
+ const parsed = asIntent(candidate.data);
296
+ if (parsed?.phase === "abandoned") abandonedCorrelations.add(parsed.correlationId);
297
+ }
298
+ let resolved;
299
+ for (const entry of priorEntries) {
300
+ if (!entry || typeof entry !== "object") continue;
301
+ const candidate = entry;
302
+ if (candidate.type !== "custom") continue;
303
+ if (candidate.customType === COGNITIVE_ROUTING_CONTROL_ENTRY) {
304
+ const data = candidate.data;
305
+ if (
306
+ data &&
307
+ typeof data === "object" &&
308
+ (data.version === 1 || data.version === 2) &&
309
+ data.epoch === intent.epoch &&
310
+ data.control === "automatic"
311
+ ) {
312
+ resolved = "automatic";
313
+ }
314
+ continue;
315
+ }
316
+ if (candidate.customType !== COGNITIVE_ROUTING_INTENT_ENTRY) continue;
317
+ const parsed = asIntent(candidate.data);
318
+ if (
319
+ !parsed ||
320
+ parsed.phase !== "prepared" ||
321
+ parsed.epoch !== intent.epoch ||
322
+ abandonedCorrelations.has(parsed.correlationId) ||
323
+ (parsed.kind !== "profile" && parsed.kind !== "activation") ||
324
+ !hasMatchingHostEntry(priorEntries, parsed) ||
325
+ !matchesBaseline(parsed, intent)
326
+ ) {
327
+ continue;
328
+ }
329
+ resolved = controlModeForIntent(parsed);
330
+ }
331
+ return resolved;
332
+ }
187
333
  function hasMatchingHostEntry(entries, intent) {
188
334
  return entries.some((entry) => {
189
335
  if (!entry || typeof entry !== "object") return false;
190
336
  const candidate = entry;
191
337
  return (
192
- candidate.type === "model_state_change" &&
193
- candidate.correlationId === intent.correlationId &&
194
- pairEquals(asPair(candidate), intent.target)
338
+ hostCorrelationIdFromEntry(candidate) === intent.correlationId &&
339
+ pairEquals(hostPairFromEntry(candidate), intent.target)
195
340
  );
196
341
  });
197
342
  }
@@ -199,30 +344,49 @@ export class CognitiveRoutingController {
199
344
  capabilityState;
200
345
  pi;
201
346
  ctx;
347
+ initialSessionStart;
202
348
  label;
203
349
  idFactory;
350
+ liveStateAuthoritative;
204
351
  lease;
205
352
  activeProfile;
206
353
  epoch;
207
354
  returnTarget;
208
355
  controlMode = "automatic";
356
+ nativeInactiveReason;
357
+ nativeBlockedReason;
358
+ transitionInFlight = false;
209
359
  branchPending = false;
210
360
  operation = Promise.resolve();
211
361
  constructor(options) {
212
362
  this.capabilityState = options.capabilityState;
213
363
  this.pi = options.pi;
214
364
  this.ctx = options.ctx;
365
+ this.initialSessionStart = options.initialSessionStart;
215
366
  this.label = options.label ?? "Cognitive Routing";
216
367
  this.idFactory = options.idFactory ?? randomUUID;
368
+ this.liveStateAuthoritative = options.liveStateAuthoritative === true;
217
369
  }
218
370
  state() {
219
- const hostPair = latestHostPair(this.ctx);
220
- const observedPair = hostPair ?? this.currentPair();
371
+ const hostPair = this.liveStateAuthoritative ? undefined : latestHostPair(this.ctx);
372
+ const observedPair = this.liveStateAuthoritative ? this.currentPair() : (hostPair ?? this.currentPair());
221
373
  const observedProfile = this.activeProfile ? profileNameForPair(this.capabilityState, observedPair) : undefined;
222
- const activeProfile = hostPair ? observedProfile : (observedProfile ?? this.activeProfile);
374
+ const activeProfile = this.liveStateAuthoritative
375
+ ? observedProfile
376
+ : hostPair
377
+ ? observedProfile
378
+ : (observedProfile ?? this.activeProfile);
379
+ const effective = this.lease !== undefined && activeProfile !== undefined && !this.branchPending;
223
380
  return {
224
- effective: this.lease !== undefined && activeProfile !== undefined && !this.branchPending,
381
+ effective,
225
382
  controlMode: this.controlMode,
383
+ ...(this.nativeBlockedReason
384
+ ? { runtimeStatus: "blocked", runtimeReason: this.nativeBlockedReason }
385
+ : this.nativeInactiveReason
386
+ ? { runtimeStatus: "inactive", runtimeReason: this.nativeInactiveReason }
387
+ : effective
388
+ ? { runtimeStatus: "active" }
389
+ : {}),
226
390
  ...(activeProfile ? { activeProfile } : {}),
227
391
  ...(this.epoch ? { epoch: this.epoch } : {}),
228
392
  ...(this.returnTarget ? { returnTarget: { ...this.returnTarget } } : {}),
@@ -273,13 +437,69 @@ export class CognitiveRoutingController {
273
437
  async reconcileBranch(mechanism = "session-tree") {
274
438
  return this.enqueue(() => this._reconcileBranch(mechanism));
275
439
  }
276
- async _activate() {
440
+ async observeNativeChange(reason, source = undefined) {
441
+ if (this.transitionInFlight || source === "restore") return { status: "ignored" };
442
+ return this.enqueue(() => this._observeNativeChange(reason, source));
443
+ }
444
+ async reconcileLiveState(reason = "Pi model state diverged") {
445
+ if (this.transitionInFlight) return { status: "ignored" };
446
+ return this.enqueue(() => this._observeNativeChange(reason));
447
+ }
448
+ recordNativeInactive(reason, pair) {
449
+ try {
450
+ this.pi.appendEntryDurable(COGNITIVE_ROUTING_INACTIVE_ENTRY, {
451
+ version: 1,
452
+ source: "user",
453
+ reason,
454
+ branchId: this.currentBranchId(),
455
+ pair,
456
+ });
457
+ } catch {
458
+ this.nativeInactiveReason = undefined;
459
+ this.nativeBlockedReason = "inactive_state_persistence_failed";
460
+ return { status: "blocked", reason: this.nativeBlockedReason };
461
+ }
462
+ this.nativeInactiveReason = reason;
463
+ this.nativeBlockedReason = undefined;
464
+ return { status: "inactive", reason };
465
+ }
466
+ async _observeNativeChange(reason, source = undefined) {
467
+ if (!this.lease || !this.activeProfile) return { status: "ignored" };
468
+ const profile = this.capabilityState.resolvedProfiles[this.activeProfile];
469
+ const current = this.currentPair();
470
+ const expected = profile
471
+ ? {
472
+ provider: profile.provider,
473
+ modelId: profile.model,
474
+ thinkingLevel: profile.effectiveThinkingLevel,
475
+ }
476
+ : undefined;
477
+ if (current && expected && pairEquals(current, expected) && source !== "set") return { status: "ignored" };
478
+ const lease = this.lease;
479
+ if (!current) {
480
+ await this.releaseLease(lease);
481
+ this.clearActiveState();
482
+ this.nativeBlockedReason = "native_state_unavailable";
483
+ return { status: "blocked", reason: this.nativeBlockedReason };
484
+ }
485
+ const result = this.recordNativeInactive(reason, current);
486
+ await this.releaseLease(lease);
487
+ this.clearActiveState();
488
+ return result;
489
+ }
490
+ async _activate(allowInactive = false, requestedProfile, requestedControlMode) {
277
491
  if (this.state().effective && this.activeProfile) {
278
492
  return { status: "active", profile: this.activeProfile };
279
493
  }
280
494
  if (!this.capabilityState.effective) {
281
495
  return { status: "inactive", reason: "not_effective" };
282
496
  }
497
+ if ((this.nativeInactiveReason || this.nativeBlockedReason) && !allowInactive) {
498
+ return {
499
+ status: "inactive",
500
+ reason: this.nativeBlockedReason ? "runtime_blocked" : "native_override",
501
+ };
502
+ }
283
503
  const sessionEntries = entriesFrom(this.ctx);
284
504
  const lifecycleIntent = latestIntent(
285
505
  sessionEntries,
@@ -288,7 +508,10 @@ export class CognitiveRoutingController {
288
508
  if (lifecycleIntent && !hasMatchingHostEntry(sessionEntries, lifecycleIntent)) {
289
509
  return { status: "inactive", reason: "pending_intent" };
290
510
  }
291
- if (lifecycleIntent?.kind === "activation") {
511
+ if (
512
+ lifecycleIntent?.kind === "activation" &&
513
+ !(allowInactive && (this.nativeInactiveReason || this.nativeBlockedReason))
514
+ ) {
292
515
  return { status: "inactive", reason: "recover_required" };
293
516
  }
294
517
  const preservedReload = lifecycleIntent?.kind === "closing" && lifecycleIntent.resumeProfile !== undefined;
@@ -301,17 +524,29 @@ export class CognitiveRoutingController {
301
524
  }
302
525
  const epoch = this.epoch ?? this.idFactory();
303
526
  const branchId = this.currentBranchId();
304
- const sessionStart = this.capabilityState.sessionStart ?? {
305
- control: "automatic",
306
- profile: "standard",
307
- };
308
- const profileName = preservedReload ? lifecycleIntent.resumeProfile : sessionStart.profile;
527
+ const sessionStart = this.initialSessionStart ??
528
+ this.capabilityState.sessionStart ?? {
529
+ control: "automatic",
530
+ profile: "reasoning",
531
+ };
532
+ let profileName;
533
+ if (requestedProfile) {
534
+ profileName = requestedProfile;
535
+ } else if (preservedReload) {
536
+ profileName = lifecycleIntent.resumeProfile;
537
+ } else if (sessionStart.control === "automatic") {
538
+ profileName = "reasoning";
539
+ } else {
540
+ profileName = sessionStart.profile;
541
+ }
309
542
  const profile = this.capabilityState.resolvedProfiles[profileName];
310
543
  if (!profile) {
311
544
  return { status: "inactive", reason: preservedReload ? "resume_profile_unavailable" : "profile_unavailable" };
312
545
  }
313
546
  let controlMode;
314
- if (preservedReload) {
547
+ if (requestedControlMode) {
548
+ controlMode = requestedControlMode;
549
+ } else if (preservedReload) {
315
550
  controlMode = lifecycleIntent.resumeControl === "manual" ? `manual-${profileName}` : "automatic";
316
551
  } else {
317
552
  controlMode = sessionStart.control === "manual" ? `manual-${profileName}` : "automatic";
@@ -365,7 +600,7 @@ export class CognitiveRoutingController {
365
600
  (candidate) => candidate.kind === "profile" && candidate.epoch === this.epoch,
366
601
  )
367
602
  : undefined;
368
- const fromPair = latestHostPair(this.ctx) ?? this.currentPair();
603
+ const fromPair = this.observedHostPair();
369
604
  const fromProfile = this.activeProfile ?? profileNameForPair(this.capabilityState, fromPair);
370
605
  const intent = this.prepareIntent({
371
606
  kind: "closing",
@@ -392,7 +627,7 @@ export class CognitiveRoutingController {
392
627
  }
393
628
  this.clearActiveState(false);
394
629
  try {
395
- const result = await lease.setState({ ...returnTarget, correlationId: intent.correlationId });
630
+ const result = await this.setState(lease, { ...returnTarget, correlationId: intent.correlationId });
396
631
  if (result.status !== "applied" && result.status !== "unchanged") {
397
632
  return { status: "inactive", reason: "restore_rejected" };
398
633
  }
@@ -403,9 +638,122 @@ export class CognitiveRoutingController {
403
638
  await this.releaseLease(lease);
404
639
  }
405
640
  }
641
+ async recoverTerminalIntent(intent) {
642
+ const commit = latestPiSessionCommit(branchEntriesFrom(this.ctx), intent.correlationId);
643
+ if (!commit) return undefined;
644
+ if (commit.phase === "prepared") {
645
+ if (!this.pi.recoverPreparedModelState) {
646
+ return { status: "pending", reason: "prepared_recovery_unsupported" };
647
+ }
648
+ let recovery;
649
+ try {
650
+ recovery = await this.pi.recoverPreparedModelState({
651
+ fromPair: commit.fromPair,
652
+ target: commit.target,
653
+ correlationId: commit.correlationId,
654
+ });
655
+ } catch {
656
+ return { status: "pending", reason: "prepared_recovery_failed" };
657
+ }
658
+ if (recovery.status === "rollback-failed") {
659
+ return { status: "pending", reason: "rollback_failed" };
660
+ }
661
+ if (recovery.status !== "restored") {
662
+ return { status: "pending", reason: "prepared_recovery_failed" };
663
+ }
664
+ try {
665
+ this.abandonPreparedIntent(intent, "Pi session transition interrupted.");
666
+ } catch {
667
+ return { status: "pending", reason: "intent_abandon_failed" };
668
+ }
669
+ const recovered = await this._recover();
670
+ return recovered.status === "inactive" && recovered.reason === "no_pending_intent"
671
+ ? { status: "inactive", reason: "intent_abandoned" }
672
+ : recovered;
673
+ }
674
+ if (commit.phase !== "aborted") return undefined;
675
+ if (commit.status === "rollback-failed") {
676
+ return { status: "pending", reason: "rollback_failed" };
677
+ }
678
+ try {
679
+ this.abandonPreparedIntent(intent, "Pi session transition rolled back.");
680
+ } catch {
681
+ return { status: "pending", reason: "intent_abandon_failed" };
682
+ }
683
+ return this._recover();
684
+ }
685
+ async recoverTerminalProfileIntent(intent) {
686
+ const commit = latestPiSessionCommit(branchEntriesFrom(this.ctx), intent.correlationId);
687
+ if (!commit || commit.phase === "committed") return undefined;
688
+ if (commit.phase === "aborted" && commit.status === "rollback-failed") {
689
+ return { status: "pending", reason: "rollback_failed" };
690
+ }
691
+ const controlMode = resolveControlBeforeIntent(branchEntriesFrom(this.ctx), intent);
692
+ if (
693
+ !controlMode ||
694
+ !intent.fromPair ||
695
+ !intent.fromProfile ||
696
+ !pairMatchesProfile(this.capabilityState, intent.fromPair, intent.fromProfile)
697
+ ) {
698
+ return { status: "pending", reason: "baseline_control_unknown" };
699
+ }
700
+ if (commit.phase === "prepared") {
701
+ if (!this.pi.recoverPreparedModelState) {
702
+ return { status: "pending", reason: "prepared_recovery_unsupported" };
703
+ }
704
+ let recovery;
705
+ try {
706
+ recovery = await this.pi.recoverPreparedModelState({
707
+ fromPair: commit.fromPair,
708
+ target: commit.target,
709
+ correlationId: commit.correlationId,
710
+ });
711
+ } catch {
712
+ return { status: "pending", reason: "prepared_recovery_failed" };
713
+ }
714
+ if (recovery.status === "rollback-failed") {
715
+ return { status: "pending", reason: "rollback_failed" };
716
+ }
717
+ if (recovery.status !== "restored") {
718
+ return { status: "pending", reason: "prepared_recovery_failed" };
719
+ }
720
+ }
721
+ try {
722
+ this.abandonPreparedIntent(intent, "Pi session transition interrupted or rolled back.");
723
+ } catch {
724
+ return { status: "pending", reason: "intent_abandon_failed" };
725
+ }
726
+ const recovered = await this.activatePrepared({
727
+ kind: "profile",
728
+ controlMode,
729
+ source: "system",
730
+ mechanism: "recovery",
731
+ ...(decisionCorrelationForIntent(intent) ? { decisionCorrelationId: decisionCorrelationForIntent(intent) } : {}),
732
+ branchId: this.currentBranchId(),
733
+ epoch: intent.epoch,
734
+ returnTarget: intent.returnTarget,
735
+ target: intent.fromPair,
736
+ profile: intent.fromProfile,
737
+ });
738
+ return recovered.status === "active" ? recovered : { status: "pending", reason: "baseline_recovery_failed" };
739
+ }
406
740
  async _recover() {
407
741
  const sessionEntries = entriesFrom(this.ctx);
408
742
  const branchEntries = branchEntriesFrom(this.ctx);
743
+ const inactive = latestInactiveEntry(branchEntries);
744
+ const branchLifecycleIntent = latestIntent(
745
+ branchEntries,
746
+ (intent) => intent.kind === "activation" || intent.kind === "closing",
747
+ );
748
+ if (
749
+ inactive &&
750
+ (!branchLifecycleIntent || intentEntryIndex(branchEntries, branchLifecycleIntent) <= inactive.position)
751
+ ) {
752
+ this.nativeInactiveReason = inactive.entry.reason;
753
+ this.nativeBlockedReason = undefined;
754
+ this.clearActiveState();
755
+ return { status: "inactive", reason: "native_override" };
756
+ }
409
757
  const lifecycleIntent = latestIntent(
410
758
  sessionEntries,
411
759
  (intent) => intent.kind === "activation" || intent.kind === "closing",
@@ -413,6 +761,10 @@ export class CognitiveRoutingController {
413
761
  if (!lifecycleIntent) {
414
762
  return { status: "inactive", reason: "no_pending_intent" };
415
763
  }
764
+ if (lifecycleIntent.kind === "activation") {
765
+ const lifecycleTerminal = await this.recoverTerminalIntent(lifecycleIntent);
766
+ if (lifecycleTerminal) return lifecycleTerminal;
767
+ }
416
768
  if (lifecycleIntent.kind === "closing") {
417
769
  return hasMatchingHostEntry(sessionEntries, lifecycleIntent)
418
770
  ? { status: "inactive", reason: "closed" }
@@ -433,6 +785,19 @@ export class CognitiveRoutingController {
433
785
  thinkingLevel: currentActivationProfile.effectiveThinkingLevel,
434
786
  };
435
787
  const activationMatched = hasMatchingHostEntry(sessionEntries, lifecycleIntent);
788
+ if (this.liveStateAuthoritative && activationMatched) {
789
+ const livePair = this.currentPair();
790
+ if (!livePair) {
791
+ this.clearActiveState();
792
+ this.nativeBlockedReason = "native_state_unavailable";
793
+ return { status: "inactive", reason: "native_override" };
794
+ }
795
+ if (!pairEquals(livePair, activationTarget)) {
796
+ this.recordNativeInactive("Pi model state diverged during recovery", livePair);
797
+ this.clearActiveState();
798
+ return { status: "inactive", reason: "native_override" };
799
+ }
800
+ }
436
801
  if (activationMatched && !pairEquals(activationTarget, lifecycleIntent.target)) {
437
802
  return { status: "pending", reason: "applied_target_changed" };
438
803
  }
@@ -442,7 +807,7 @@ export class CognitiveRoutingController {
442
807
  if (!activationMatched) {
443
808
  const retried = await this.activatePrepared({
444
809
  kind: "activation",
445
- controlMode: "automatic",
810
+ controlMode: latestControlMode(branchEntries, lifecycleIntent),
446
811
  source: "system",
447
812
  mechanism: "recovery",
448
813
  branchId: this.currentBranchId(),
@@ -458,6 +823,8 @@ export class CognitiveRoutingController {
458
823
  (intent) => intent.kind === "profile" && intent.epoch === lifecycleIntent.epoch,
459
824
  );
460
825
  if (profileIntent) {
826
+ const terminal = await this.recoverTerminalProfileIntent(profileIntent);
827
+ if (terminal) return terminal;
461
828
  const currentProfile = this.capabilityState.resolvedProfiles[profileIntent.profile ?? "standard"];
462
829
  if (!currentProfile) {
463
830
  return this.abandonIntent(profileIntent, "Prepared profile is unavailable.");
@@ -491,7 +858,7 @@ export class CognitiveRoutingController {
491
858
  }
492
859
  return this.activatePrepared({
493
860
  kind: "activation",
494
- controlMode: "automatic",
861
+ controlMode: latestControlMode(branchEntries, lifecycleIntent),
495
862
  source: "system",
496
863
  mechanism: "recovery",
497
864
  branchId: this.currentBranchId(),
@@ -502,7 +869,7 @@ export class CognitiveRoutingController {
502
869
  });
503
870
  }
504
871
  async recoverClosingIntent(intent) {
505
- const fromPair = latestHostPair(this.ctx) ?? this.currentPair();
872
+ const fromPair = this.observedHostPair();
506
873
  const fromProfile = intent.fromProfile ?? profileNameForPair(this.capabilityState, fromPair);
507
874
  const retryIntent = this.prepareIntent({
508
875
  kind: "closing",
@@ -536,7 +903,10 @@ export class CognitiveRoutingController {
536
903
  return { status: "pending", reason: `closing_recovery_acquire_${acquisition.status}` };
537
904
  }
538
905
  try {
539
- const result = await acquisition.lease.setState({ ...intent.target, correlationId: retryIntent.correlationId });
906
+ const result = await this.setState(acquisition.lease, {
907
+ ...intent.target,
908
+ correlationId: retryIntent.correlationId,
909
+ });
540
910
  return result.status === "applied" || result.status === "unchanged"
541
911
  ? { status: "inactive", reason: "restored" }
542
912
  : { status: "pending", reason: "closing_recovery_rejected" };
@@ -546,20 +916,23 @@ export class CognitiveRoutingController {
546
916
  await this.releaseLease(acquisition.lease);
547
917
  }
548
918
  }
919
+ abandonPreparedIntent(intent, reason) {
920
+ const fromPair = intent.fromPair ?? this.observedHostPair();
921
+ const fromProfile = intent.fromProfile ?? profileNameForPair(this.capabilityState, fromPair);
922
+ this.pi.appendEntryDurable(COGNITIVE_ROUTING_INTENT_ENTRY, {
923
+ ...intent,
924
+ version: 2,
925
+ phase: "abandoned",
926
+ source: "system",
927
+ mechanism: intent.mechanism ?? "recovery",
928
+ ...(fromPair ? { fromPair } : {}),
929
+ ...(fromProfile ? { fromProfile } : {}),
930
+ reason,
931
+ });
932
+ }
549
933
  async abandonIntent(intent, reason) {
550
934
  try {
551
- const fromPair = intent.fromPair ?? latestHostPair(this.ctx) ?? this.currentPair();
552
- const fromProfile = intent.fromProfile ?? profileNameForPair(this.capabilityState, fromPair);
553
- this.pi.appendEntryDurable(COGNITIVE_ROUTING_INTENT_ENTRY, {
554
- ...intent,
555
- version: 2,
556
- phase: "abandoned",
557
- source: "system",
558
- mechanism: intent.mechanism ?? "recovery",
559
- ...(fromPair ? { fromPair } : {}),
560
- ...(fromProfile ? { fromProfile } : {}),
561
- reason,
562
- });
935
+ this.abandonPreparedIntent(intent, reason);
563
936
  const recovered = await this._recover();
564
937
  return recovered.status === "inactive" && recovered.reason === "no_pending_intent"
565
938
  ? { status: "inactive", reason: "intent_abandoned" }
@@ -639,8 +1012,9 @@ export class CognitiveRoutingController {
639
1012
  if (!this.capabilityState.effective) {
640
1013
  return { status: "inactive", reason: "not_effective" };
641
1014
  }
642
- const activation = this.state().effective ? { status: "active" } : await this._activate();
643
- if (activation.status !== "active") return activation;
1015
+ if (!this.state().effective) {
1016
+ return this._activate(true, profile, `manual-${profile}`);
1017
+ }
644
1018
  return this._setProfile(profile, {
645
1019
  controlMode: `manual-${profile}`,
646
1020
  source: "user",
@@ -671,7 +1045,7 @@ export class CognitiveRoutingController {
671
1045
  modelId: resolvedProfile.model,
672
1046
  thinkingLevel: resolvedProfile.effectiveThinkingLevel,
673
1047
  };
674
- const fromPair = latestHostPair(this.ctx) ?? this.currentPair();
1048
+ const fromPair = this.observedHostPair();
675
1049
  if (!fromPair) return { status: "inactive", reason: "current_state_unavailable" };
676
1050
  const fromProfile = this.activeProfile ?? profileNameForPair(this.capabilityState, fromPair);
677
1051
  const correlationId = this.idFactory();
@@ -686,6 +1060,7 @@ export class CognitiveRoutingController {
686
1060
  ...(decisionCorrelationId ? { decisionCorrelationId } : {}),
687
1061
  ...(fromPair ? { fromPair } : {}),
688
1062
  ...(fromProfile ? { fromProfile } : {}),
1063
+ fromControl: options.controlMode === "automatic" ? "automatic" : "manual",
689
1064
  reason: options.reason,
690
1065
  branchId: this.currentBranchId(),
691
1066
  epoch: this.epoch,
@@ -714,8 +1089,27 @@ export class CognitiveRoutingController {
714
1089
  return { status: "inactive", reason: "prepare_failed" };
715
1090
  }
716
1091
  try {
717
- const result = await lease.setState({ ...target, correlationId: intent.correlationId });
1092
+ const result = await this.setState(lease, { ...target, correlationId: intent.correlationId });
718
1093
  if (result.status !== "applied" && result.status !== "unchanged") {
1094
+ const commit = latestPiSessionCommit(branchEntriesFrom(this.ctx), intent.correlationId);
1095
+ if (result.status === "rejected" && commit?.phase === "aborted" && commit.status === "rollback-failed") {
1096
+ try {
1097
+ this.abandonPreparedIntent(intent, "Pi session rollback failed.");
1098
+ } catch {
1099
+ // Fail closed even when the terminal abandonment cannot be persisted.
1100
+ }
1101
+ await this.releaseLease(lease);
1102
+ this.clearActiveState();
1103
+ return { status: "inactive", reason: "rollback_failed" };
1104
+ }
1105
+ if (result.status === "rejected" && commit?.phase === "aborted" && commit.status === "rolled-back") {
1106
+ try {
1107
+ this.abandonPreparedIntent(intent, "Pi session transition rolled back.");
1108
+ this.branchPending = false;
1109
+ } catch {
1110
+ // Preserve the pending boundary when the terminal abandonment cannot be persisted.
1111
+ }
1112
+ }
719
1113
  return { status: "inactive", reason: "transition_rejected" };
720
1114
  }
721
1115
  this.activeProfile = profile;
@@ -728,8 +1122,27 @@ export class CognitiveRoutingController {
728
1122
  }
729
1123
  async _setAutomaticControl(mechanism) {
730
1124
  if (!this.lease || !this.epoch) {
731
- return { status: "rejected", reason: "not_active" };
1125
+ if (!this.nativeInactiveReason && !this.nativeBlockedReason) return { status: "rejected", reason: "not_active" };
1126
+ const activation = await this._activate(true, this.profileForCurrentPair(), "automatic");
1127
+ return activation.status === "active"
1128
+ ? { status: "automatic" }
1129
+ : { status: "rejected", reason: activation.reason };
1130
+ }
1131
+ const current = this.state();
1132
+ if (!current.activeProfile) {
1133
+ return { status: "rejected", reason: "profile_unknown" };
732
1134
  }
1135
+ if (current.activeProfile !== "reasoning") {
1136
+ const transition = await this._setProfile("reasoning", {
1137
+ controlMode: "automatic",
1138
+ source: "user",
1139
+ mechanism,
1140
+ reason: "automatic control selects the Reasoning profile",
1141
+ });
1142
+ if (transition.status !== "active") return { status: "rejected", reason: transition.reason };
1143
+ return { status: "automatic" };
1144
+ }
1145
+ if (this.controlMode === "automatic") return { status: "automatic" };
733
1146
  try {
734
1147
  this.pi.appendEntryDurable(COGNITIVE_ROUTING_CONTROL_ENTRY, {
735
1148
  version: 2,
@@ -757,6 +1170,13 @@ export class CognitiveRoutingController {
757
1170
  return undefined;
758
1171
  }
759
1172
  }
1173
+ observedHostPair() {
1174
+ const livePair = this.currentPair();
1175
+ return this.liveStateAuthoritative ? livePair : (latestHostPair(this.ctx) ?? livePair);
1176
+ }
1177
+ profileForCurrentPair() {
1178
+ return profileNameForPair(this.capabilityState, this.currentPair());
1179
+ }
760
1180
  currentPair() {
761
1181
  const model = this.ctx.model;
762
1182
  if (
@@ -777,7 +1197,7 @@ export class CognitiveRoutingController {
777
1197
  return { version: 2, phase: "prepared", ...input };
778
1198
  }
779
1199
  async activatePrepared(options) {
780
- const fromPair = latestHostPair(this.ctx) ?? this.currentPair();
1200
+ const fromPair = this.observedHostPair();
781
1201
  if (!fromPair) return { status: "inactive", reason: "current_state_unavailable" };
782
1202
  const fromProfile = profileNameForPair(this.capabilityState, fromPair);
783
1203
  const correlationId = this.idFactory();
@@ -816,7 +1236,7 @@ export class CognitiveRoutingController {
816
1236
  }
817
1237
  this.lease = acquisition.lease;
818
1238
  try {
819
- const result = await acquisition.lease.setState({ ...options.target, correlationId: intent.correlationId });
1239
+ const result = await this.setState(acquisition.lease, { ...options.target, correlationId: intent.correlationId });
820
1240
  if (result.status !== "applied" && result.status !== "unchanged") {
821
1241
  await this.releaseLease(acquisition.lease);
822
1242
  this.clearActiveState();
@@ -826,6 +1246,8 @@ export class CognitiveRoutingController {
826
1246
  this.returnTarget = options.returnTarget;
827
1247
  this.activeProfile = options.profile;
828
1248
  this.controlMode = options.controlMode;
1249
+ this.nativeInactiveReason = undefined;
1250
+ this.nativeBlockedReason = undefined;
829
1251
  return { status: "active", profile: options.profile };
830
1252
  } catch {
831
1253
  await this.releaseLease(acquisition.lease);
@@ -841,6 +1263,14 @@ export class CognitiveRoutingController {
841
1263
  );
842
1264
  return next;
843
1265
  }
1266
+ async setState(lease, request) {
1267
+ this.transitionInFlight = true;
1268
+ try {
1269
+ return await lease.setState(request);
1270
+ } finally {
1271
+ this.transitionInFlight = false;
1272
+ }
1273
+ }
844
1274
  async releaseLease(lease) {
845
1275
  try {
846
1276
  await lease.release();