@hassangameryt/freeflow 0.7.1 → 0.7.3
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.
- package/.claude-plugin/marketplace.json +1 -1
- package/.claude-plugin/plugin.json +1 -1
- package/.codex-plugin/plugin.json +1 -1
- package/.cursor-plugin/plugin.json +1 -1
- package/README.md +153 -128
- package/capabilities/cognitive-routing/SKILL.md +126 -274
- package/command-surface.json +7 -1
- package/gemini-extension.json +1 -1
- package/package.json +4 -4
- package/pi-extension/dist/cognitive-routing-v2/budget.js +42 -0
- package/pi-extension/dist/cognitive-routing-v2/config.js +136 -0
- package/pi-extension/dist/cognitive-routing-v2/projection.js +330 -0
- package/pi-extension/dist/cognitive-routing-v2/render.js +252 -0
- package/pi-extension/dist/cognitive-routing-v2/runtime.js +2181 -0
- package/pi-extension/dist/cognitive-routing-v2/schemas.js +173 -0
- package/pi-extension/dist/cognitive-routing-v2/state.js +828 -0
- package/pi-extension/dist/cognitive-routing-v2/tools.js +60 -0
- package/pi-extension/dist/cognitive-routing-v2/types.js +48 -0
- package/pi-extension/dist/context-virtualization/runtime.js +29 -7
- package/pi-extension/dist/freeflow-context/tool.js +12 -6
- package/pi-extension/dist/index.js +302 -626
- package/pi-extension/dist/provider-support/astra/adapter.js +110 -0
- package/pi-extension/dist/provider-support/astra/history.js +125 -0
- package/pi-extension/dist/provider-support/astra/index.js +11 -0
- package/pi-extension/dist/provider-support/astra/session-state.js +114 -0
- package/pi-extension/dist/provider-support/index.js +4 -0
- package/pi-extension/dist/runtime/request-history.js +216 -0
- package/pi-extension/dist/runtime/runtime-context.js +85 -16
- package/pi-extension/dist/session-sources/events.js +217 -0
- package/pi-extension/dist/session-sources/provenance.js +32 -0
- package/pi-extension/dist/session-sources/read-only-session.js +148 -0
- package/pi-extension/dist/session-sources/sources.js +277 -0
- package/pi-extension/dist/settings/settings-ui.js +274 -147
- package/pi-extension/src/cognitive-routing-v2/budget.ts +42 -0
- package/pi-extension/src/cognitive-routing-v2/config.ts +173 -0
- package/pi-extension/src/cognitive-routing-v2/projection.ts +375 -0
- package/pi-extension/src/cognitive-routing-v2/render.ts +277 -0
- package/pi-extension/src/cognitive-routing-v2/runtime.ts +2241 -0
- package/pi-extension/src/cognitive-routing-v2/schemas.ts +180 -0
- package/pi-extension/src/cognitive-routing-v2/state.ts +839 -0
- package/pi-extension/src/cognitive-routing-v2/tools.ts +62 -0
- package/pi-extension/src/cognitive-routing-v2/types.ts +254 -0
- package/pi-extension/src/context-virtualization/runtime.ts +30 -8
- package/pi-extension/src/freeflow-context/tool.ts +12 -6
- package/pi-extension/src/index.ts +308 -675
- package/pi-extension/src/provider-support/astra/adapter.ts +108 -0
- package/pi-extension/src/provider-support/astra/history.ts +145 -0
- package/pi-extension/src/provider-support/astra/index.ts +12 -0
- package/pi-extension/src/provider-support/astra/session-state.ts +113 -0
- package/pi-extension/src/provider-support/index.ts +5 -0
- package/pi-extension/src/runtime/request-history.ts +243 -0
- package/pi-extension/src/runtime/runtime-context.ts +100 -17
- package/pi-extension/src/session-sources/events.ts +230 -0
- package/pi-extension/src/session-sources/provenance.ts +39 -0
- package/pi-extension/src/session-sources/read-only-session.ts +170 -0
- package/pi-extension/src/session-sources/sources.ts +334 -0
- package/pi-extension/src/settings/settings-ui.ts +311 -158
- package/pi-extension/tests/cache-reuse/history.test.js +117 -0
- package/pi-extension/tests/cache-reuse/native.test.js +91 -0
- package/pi-extension/tests/cache-reuse/settings.test.js +211 -0
- package/pi-extension/tests/cognitive-routing-v2/communication.test.js +577 -0
- package/pi-extension/tests/cognitive-routing-v2/compaction-input.test.js +183 -0
- package/pi-extension/tests/cognitive-routing-v2/delegation.test.js +557 -0
- package/pi-extension/tests/cognitive-routing-v2/native.test.js +474 -0
- package/pi-extension/tests/cognitive-routing-v2/performance.test.js +87 -0
- package/pi-extension/tests/cognitive-routing-v2/persistence.test.js +300 -0
- package/pi-extension/tests/cognitive-routing-v2/projection.test.js +241 -0
- package/pi-extension/tests/cognitive-routing-v2/recovery.test.js +1034 -0
- package/pi-extension/tests/cognitive-routing-v2/render.test.js +244 -0
- package/pi-extension/tests/cognitive-routing-v2/runtime.test.js +423 -0
- package/pi-extension/tests/cognitive-routing-v2/stabilization.test.js +521 -0
- package/pi-extension/tests/cognitive-routing-v2/state.test.js +586 -0
- package/pi-extension/tests/context-virtualization/extension.test.js +33 -7
- package/pi-extension/tests/context-virtualization/runtime.test.js +17 -3
- package/pi-extension/tests/fixtures/routing-native.js +191 -0
- package/pi-extension/tests/integration/mode-free-core.test.js +6 -4
- package/pi-extension/tests/integration/pi-extension.test.js +655 -59
- package/pi-extension/tests/integration/prompt-architecture.test.js +127 -30
- package/pi-extension/tests/integration/unaffected-regressions.test.js +78 -11
- package/pi-extension/tests/provider-support/astra-boundaries.test.js +75 -0
- package/pi-extension/tests/provider-support/astra.test.js +306 -0
- package/pi-extension/tests/provider-support/cli.test.js +92 -0
- package/pi-extension/tests/provider-support/native.test.js +183 -0
- package/plugin.json +1 -1
- package/runtime/prompts/cognitive-routing.md +4 -5
- package/runtime/prompts/core.md +51 -45
- package/runtime/prompts/interaction-contract.md +5 -5
- package/skills/action-selection/SKILL.md +57 -97
- package/skills/design-for-depth/SKILL.md +58 -193
- package/skills/design-for-depth/references/implementation-decisions.md +76 -0
- package/skills/diagnose-failure/SKILL.md +64 -196
- package/skills/discuss/SKILL.md +64 -123
- package/skills/discuss/references/discussion-continuity.md +7 -3
- package/skills/execute-work/SKILL.md +69 -88
- package/skills/execute-work/references/execute-work-edges.md +9 -3
- package/skills/handoff/SKILL.md +44 -54
- package/skills/handoff/references/handoff-templates.md +15 -6
- package/skills/review-artifact/SKILL.md +80 -127
- package/skills/review-artifact/references/independent-artifact-reviewer-contract.md +1 -1
- package/skills/review-work/SKILL.md +65 -115
- package/skills/review-work/references/independent-work-reviewer-contract.md +1 -1
- package/skills/simplify-code/SKILL.md +47 -100
- package/skills/track-work/SKILL.md +128 -119
- package/skills/track-work/references/working-record-format.md +14 -2
- package/skills/verify-work/SKILL.md +58 -62
- package/skills/verify-work/references/integration-evidence.md +4 -2
- package/skills/verify-work/references/test-design.md +89 -0
- package/skills/workflow/SKILL.md +73 -119
- package/skills/write-plan/SKILL.md +46 -77
- package/skills/write-plan/references/plan-shapes.md +5 -5
- package/skills/write-spec/SKILL.md +51 -63
- package/skills/write-spec/references/artifact-standards.md +2 -2
- package/skills/write-spec/references/decision-records.md +3 -2
- package/skills/write-spec/references/spec-shapes.md +15 -0
- package/pi-extension/dist/cognitive-routing/commands.js +0 -96
- package/pi-extension/dist/cognitive-routing/config.js +0 -232
- package/pi-extension/dist/cognitive-routing/controller.js +0 -1292
- package/pi-extension/dist/cognitive-routing/history.js +0 -451
- package/pi-extension/dist/cognitive-routing/host.js +0 -188
- package/pi-extension/dist/cognitive-routing/pi-routing-session.js +0 -414
- package/pi-extension/dist/cognitive-routing/pi-session-control.js +0 -320
- package/pi-extension/dist/cognitive-routing/runtime.js +0 -43
- package/pi-extension/dist/cognitive-routing/tool.js +0 -212
- package/pi-extension/dist/cognitive-routing/types.js +0 -6
- package/pi-extension/src/cognitive-routing/commands.ts +0 -129
- package/pi-extension/src/cognitive-routing/config.ts +0 -303
- package/pi-extension/src/cognitive-routing/controller.ts +0 -1628
- package/pi-extension/src/cognitive-routing/history.ts +0 -650
- package/pi-extension/src/cognitive-routing/host.ts +0 -266
- package/pi-extension/src/cognitive-routing/pi-routing-session.ts +0 -538
- package/pi-extension/src/cognitive-routing/pi-session-control.ts +0 -444
- package/pi-extension/src/cognitive-routing/runtime.ts +0 -57
- package/pi-extension/src/cognitive-routing/tool.ts +0 -273
- package/pi-extension/src/cognitive-routing/types.ts +0 -89
- package/pi-extension/tests/cognitive-routing/commands.test.js +0 -134
- package/pi-extension/tests/cognitive-routing/controller.test.js +0 -1128
- package/pi-extension/tests/cognitive-routing/extension-lifecycle.test.js +0 -1142
- package/pi-extension/tests/cognitive-routing/history.test.js +0 -274
- package/pi-extension/tests/cognitive-routing/host-identity.test.js +0 -63
- package/pi-extension/tests/cognitive-routing/pi-normal-lifecycle.test.js +0 -434
- package/pi-extension/tests/cognitive-routing/pi-session-control.test.js +0 -174
- package/pi-extension/tests/cognitive-routing/pi-session-history.test.js +0 -91
- package/pi-extension/tests/cognitive-routing/profile-resolution.test.js +0 -279
- package/pi-extension/tests/cognitive-routing/tool.test.js +0 -230
- package/skills/tdd/SKILL.md +0 -143
- package/skills/tdd/references/test-design.md +0 -96
- /package/pi-extension/tests/{cognitive-routing/host-fixture.js → fixtures/pi-host.js} +0 -0
|
@@ -11,8 +11,8 @@ import {
|
|
|
11
11
|
setSessionCoreOverride,
|
|
12
12
|
} from "../runtime/runtime-context.js";
|
|
13
13
|
import { PiSettingsComponent } from "./settings-tui.js";
|
|
14
|
-
import { supportsPiSessionModelStateApi } from "../cognitive-routing/pi-session-control.js";
|
|
15
14
|
import { isPiFlowHost } from "../runtime/runtime-identity.js";
|
|
15
|
+
import { isWorkerProfile, workersForDelegation } from "../cognitive-routing-v2/types.js";
|
|
16
16
|
const DEFAULT_FREEFLOW_ENABLED = true;
|
|
17
17
|
const DEFAULT_CONTEXT_VIRTUALIZATION_ENABLED = false;
|
|
18
18
|
const DEFAULT_CONVERSATION_HISTORY_ENABLED = false;
|
|
@@ -102,7 +102,7 @@ function effectiveItemValue(item) {
|
|
|
102
102
|
function formatCoreValue(value) {
|
|
103
103
|
if (typeof value === "boolean") return booleanValue(value);
|
|
104
104
|
if (isRecord(value) && typeof value.provider === "string" && typeof value.model === "string") {
|
|
105
|
-
const effort = typeof value.
|
|
105
|
+
const effort = typeof value.thinking === "string" ? ` · ${value.thinking}` : "";
|
|
106
106
|
return `${value.provider}/${value.model}${effort}`;
|
|
107
107
|
}
|
|
108
108
|
return String(value ?? "");
|
|
@@ -192,6 +192,68 @@ function createScopedBooleanItem(options) {
|
|
|
192
192
|
item.displaySuffix = coreDisplaySuffix(item);
|
|
193
193
|
return item;
|
|
194
194
|
}
|
|
195
|
+
function createScopedDelegationItem(options) {
|
|
196
|
+
const path = ["cognitiveRouting", "delegation"];
|
|
197
|
+
const repositoryValue = getPath(options.rawConfig, path);
|
|
198
|
+
const inheritedValue = ["executor", "helper", "both"].includes(repositoryValue) ? repositoryValue : "executor";
|
|
199
|
+
const inheritedSource = repositoryValue === undefined ? "builtin" : "repository";
|
|
200
|
+
const localValue = getPath(options.localConfig, path);
|
|
201
|
+
const descriptions = {
|
|
202
|
+
executor: "Coordinator delegates assignments to Executor.",
|
|
203
|
+
helper: "Coordinator delegates supporting assignments to Helper and keeps production implementation.",
|
|
204
|
+
both: "Coordinator chooses Helper or Executor for each assignment.",
|
|
205
|
+
};
|
|
206
|
+
const item =
|
|
207
|
+
options.scope === "local"
|
|
208
|
+
? {
|
|
209
|
+
id: "freeflow.cognitiveRouting.delegation",
|
|
210
|
+
label: "Delegation method",
|
|
211
|
+
description:
|
|
212
|
+
"Choose enabled workers for automatic routing. Inherit uses the repository value; this setting does not rank model presets.",
|
|
213
|
+
path,
|
|
214
|
+
kind: "enum",
|
|
215
|
+
value: ["executor", "helper", "both"].includes(localValue) ? localValue : LOCAL_INHERIT,
|
|
216
|
+
values: [LOCAL_INHERIT, "executor", "helper", "both"],
|
|
217
|
+
valueLabels: {
|
|
218
|
+
inherit: "Inherit repository",
|
|
219
|
+
executor: "Executor only",
|
|
220
|
+
helper: "Helper only",
|
|
221
|
+
both: "Helper and Executor",
|
|
222
|
+
},
|
|
223
|
+
valueDescriptions: {
|
|
224
|
+
inherit: `Use ${inheritedValue} from ${inheritedSource}.`,
|
|
225
|
+
...descriptions,
|
|
226
|
+
},
|
|
227
|
+
configScope: "local",
|
|
228
|
+
configValues: { inherit: undefined, executor: "executor", helper: "helper", both: "both" },
|
|
229
|
+
effectiveValue: options.effectiveValue,
|
|
230
|
+
effectiveSource: options.effectiveSource,
|
|
231
|
+
inheritedValue,
|
|
232
|
+
inheritedSource,
|
|
233
|
+
}
|
|
234
|
+
: {
|
|
235
|
+
id: "freeflow.cognitiveRouting.delegation",
|
|
236
|
+
label: "Delegation method",
|
|
237
|
+
description: "Choose enabled workers for automatic routing. This edits shared .freeflow/config.json.",
|
|
238
|
+
path,
|
|
239
|
+
kind: "enum",
|
|
240
|
+
value: inheritedValue,
|
|
241
|
+
values: ["executor", "helper", "both"],
|
|
242
|
+
valueLabels: {
|
|
243
|
+
executor: "Executor only",
|
|
244
|
+
helper: "Helper only",
|
|
245
|
+
both: "Helper and Executor",
|
|
246
|
+
},
|
|
247
|
+
valueDescriptions: descriptions,
|
|
248
|
+
defaultValue: "executor",
|
|
249
|
+
configScope: "repository",
|
|
250
|
+
effectiveValue: options.effectiveValue,
|
|
251
|
+
effectiveSource: options.effectiveSource,
|
|
252
|
+
localOverrideValue: ["executor", "helper", "both"].includes(localValue) ? localValue : undefined,
|
|
253
|
+
};
|
|
254
|
+
item.displaySuffix = coreDisplaySuffix(item);
|
|
255
|
+
return item;
|
|
256
|
+
}
|
|
195
257
|
function resolveSettingsCoreView(rawConfig, layers) {
|
|
196
258
|
const localConfig = layers?.local.valid && isRecord(layers.local.parsed) ? layers.local.parsed : {};
|
|
197
259
|
const fallbackCore = {
|
|
@@ -248,7 +310,7 @@ function createSessionBooleanItem(options) {
|
|
|
248
310
|
item.displaySuffix = coreDisplaySuffix(item);
|
|
249
311
|
return item;
|
|
250
312
|
}
|
|
251
|
-
function sessionFreeflowItems(state, cognitiveRoutingController) {
|
|
313
|
+
function sessionFreeflowItems(state, cognitiveRoutingController, ctx) {
|
|
252
314
|
const sessionOverrides = state.sessionOverrides;
|
|
253
315
|
const configured = state.configuredCoreConfig;
|
|
254
316
|
const configuredSources = state.configuredSources;
|
|
@@ -290,47 +352,76 @@ function sessionFreeflowItems(state, cognitiveRoutingController) {
|
|
|
290
352
|
contextVirtualizationItem.inactive = freeflowInactive;
|
|
291
353
|
conversationHistoryItem.inactive = freeflowInactive;
|
|
292
354
|
const cognitiveRoutingState = cognitiveRoutingController?.state();
|
|
293
|
-
const
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
355
|
+
const delegation = state.cognitiveRouting?.delegation ?? "executor";
|
|
356
|
+
const heldProfile = cognitiveRoutingState?.controlMode.startsWith("manual-")
|
|
357
|
+
? cognitiveRoutingState.activeProfile
|
|
358
|
+
: undefined;
|
|
359
|
+
const availableProfiles = ["coordinator", ...workersForDelegation(delegation)];
|
|
360
|
+
if (heldProfile === "coordinator" || isWorkerProfile(heldProfile)) availableProfiles.push(heldProfile);
|
|
361
|
+
const manualProfiles = [...new Set(availableProfiles)];
|
|
362
|
+
const cognitiveRoutingProfile = heldProfile && manualProfiles.includes(heldProfile) ? heldProfile : "auto";
|
|
299
363
|
const cognitiveRoutingItem = state.cognitiveRouting
|
|
300
364
|
? {
|
|
301
365
|
id: "freeflow.cognitiveRouting.profile",
|
|
302
366
|
label: "Cognitive Routing",
|
|
303
|
-
description: "Hold
|
|
367
|
+
description: "Hold an enabled profile manually or release the hold for automatic model control.",
|
|
304
368
|
kind: "enum",
|
|
305
369
|
value: cognitiveRoutingProfile,
|
|
306
|
-
values: ["auto",
|
|
307
|
-
valueLabels:
|
|
308
|
-
auto
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
reasoning: "Hold the reasoning profile until /freeflow profile auto.",
|
|
316
|
-
},
|
|
370
|
+
values: ["auto", ...manualProfiles],
|
|
371
|
+
valueLabels: Object.fromEntries([
|
|
372
|
+
["auto", "automatic"],
|
|
373
|
+
...manualProfiles.map((profile) => [profile, `manual · ${profile}`]),
|
|
374
|
+
]),
|
|
375
|
+
valueDescriptions: Object.fromEntries([
|
|
376
|
+
["auto", "Release the manual hold and return to Coordinator reconciliation."],
|
|
377
|
+
...manualProfiles.map((profile) => [profile, `Hold the ${profile} profile until /freeflow profile auto.`]),
|
|
378
|
+
]),
|
|
317
379
|
inactive: freeflowInactive || !state.cognitiveRouting.effective || cognitiveRoutingController === undefined,
|
|
318
380
|
runtimeInactive: state.cognitiveRouting.blockingReason?.code === "runtime_disabled",
|
|
319
381
|
displaySuffix: cognitiveRoutingState?.effective ? cognitiveRoutingProfile : "unavailable",
|
|
320
382
|
}
|
|
321
383
|
: undefined;
|
|
384
|
+
const sessionProfiles = cognitiveRoutingController?.sessionProfileOverrides();
|
|
385
|
+
const profileItems =
|
|
386
|
+
state.cognitiveRouting && cognitiveRoutingController
|
|
387
|
+
? manualProfiles.map((name) =>
|
|
388
|
+
cognitiveRoutingProfileItem({
|
|
389
|
+
name,
|
|
390
|
+
scope: "session",
|
|
391
|
+
rawConfig: {},
|
|
392
|
+
localConfig: {},
|
|
393
|
+
capabilityState: state.cognitiveRouting,
|
|
394
|
+
ctx,
|
|
395
|
+
sessionProfiles,
|
|
396
|
+
}),
|
|
397
|
+
)
|
|
398
|
+
: [];
|
|
399
|
+
const cognitiveRoutingPresets = profileItems.length
|
|
400
|
+
? {
|
|
401
|
+
id: "freeflow.cognitiveRouting.presets",
|
|
402
|
+
label: "Cognitive Routing presets",
|
|
403
|
+
description: "Temporarily choose complete model/effort pairs for profiles enabled in this Pi session.",
|
|
404
|
+
kind: "group",
|
|
405
|
+
value: profileItems.some((item) => item.value !== LOCAL_INHERIT && item.value !== undefined),
|
|
406
|
+
displaySuffix: `${profileItems.filter((item) => item.value !== LOCAL_INHERIT && item.value !== undefined).length}/${profileItems.length} session overrides`,
|
|
407
|
+
inactive: freeflowInactive || !state.cognitiveRouting?.effective || cognitiveRoutingController === undefined,
|
|
408
|
+
children: profileItems,
|
|
409
|
+
}
|
|
410
|
+
: undefined;
|
|
322
411
|
return [
|
|
323
412
|
freeflowItem,
|
|
324
413
|
...(cognitiveRoutingItem ? [cognitiveRoutingItem] : []),
|
|
414
|
+
...(cognitiveRoutingPresets ? [cognitiveRoutingPresets] : []),
|
|
325
415
|
{
|
|
326
416
|
id: "freeflow.session.reset",
|
|
327
417
|
label: "Reset session overrides",
|
|
328
|
-
description:
|
|
418
|
+
description:
|
|
419
|
+
"Clear Freeflow, Context Virtualization, Conversation History, and routing profile overrides for this Pi session.",
|
|
329
420
|
kind: "enum",
|
|
330
421
|
value: "available",
|
|
331
422
|
values: ["reset"],
|
|
332
423
|
valueLabels: { reset: "Reset all session overrides" },
|
|
333
|
-
valueDescriptions: { reset: "Return every session setting to its configured value." },
|
|
424
|
+
valueDescriptions: { reset: "Return every session setting and routing preset to its configured value." },
|
|
334
425
|
format: () => "available",
|
|
335
426
|
transient: true,
|
|
336
427
|
},
|
|
@@ -363,8 +454,8 @@ function isCognitiveRoutingProfile(value) {
|
|
|
363
454
|
isRecord(value) &&
|
|
364
455
|
typeof value.provider === "string" &&
|
|
365
456
|
typeof value.model === "string" &&
|
|
366
|
-
typeof value.
|
|
367
|
-
COGNITIVE_ROUTING_THINKING_LEVELS.includes(value.
|
|
457
|
+
typeof value.thinking === "string" &&
|
|
458
|
+
COGNITIVE_ROUTING_THINKING_LEVELS.includes(value.thinking)
|
|
368
459
|
);
|
|
369
460
|
}
|
|
370
461
|
function cognitiveRoutingThinkingLevels(model, registry) {
|
|
@@ -413,12 +504,14 @@ function cognitiveRoutingProfileDisplay(value) {
|
|
|
413
504
|
return isCognitiveRoutingProfile(value) ? formatCoreValue(value) : "not configured";
|
|
414
505
|
}
|
|
415
506
|
function cognitiveRoutingSettingsSource(source) {
|
|
507
|
+
if (source === "session") return "session";
|
|
416
508
|
if (source === "repository") return "repository";
|
|
417
509
|
if (source === "personal" || source === "local") return "local";
|
|
418
510
|
return "builtin";
|
|
419
511
|
}
|
|
420
512
|
function cognitiveRoutingProfileDisplaySuffix(source, scope) {
|
|
421
513
|
if (!source) return undefined;
|
|
514
|
+
if (source === "session") return "(session override)";
|
|
422
515
|
if (source === "personal" && scope === "repository") return "(effective personal)";
|
|
423
516
|
return `(${source})`;
|
|
424
517
|
}
|
|
@@ -437,7 +530,16 @@ function cognitiveRoutingConfirmStep(summary) {
|
|
|
437
530
|
selectedKey: "__save__",
|
|
438
531
|
};
|
|
439
532
|
}
|
|
440
|
-
function createCognitiveRoutingProfileWizard(
|
|
533
|
+
function createCognitiveRoutingProfileWizard(
|
|
534
|
+
currentValue,
|
|
535
|
+
models,
|
|
536
|
+
allowInherit,
|
|
537
|
+
inheritChoice = {
|
|
538
|
+
label: "Inherit repository preset",
|
|
539
|
+
description: "Remove the personal override without changing the repository preset.",
|
|
540
|
+
summary: "inherit repository preset",
|
|
541
|
+
},
|
|
542
|
+
) {
|
|
441
543
|
const currentProfile = isCognitiveRoutingProfile(currentValue) ? currentValue : undefined;
|
|
442
544
|
const currentModelKey = currentProfile
|
|
443
545
|
? cognitiveRoutingModelKey(currentProfile.provider, currentProfile.model)
|
|
@@ -448,8 +550,8 @@ function createCognitiveRoutingProfileWizard(currentValue, models, allowInherit)
|
|
|
448
550
|
{
|
|
449
551
|
key: LOCAL_INHERIT,
|
|
450
552
|
value: LOCAL_INHERIT,
|
|
451
|
-
label:
|
|
452
|
-
description:
|
|
553
|
+
label: inheritChoice.label,
|
|
554
|
+
description: inheritChoice.description,
|
|
453
555
|
},
|
|
454
556
|
]
|
|
455
557
|
: []),
|
|
@@ -483,12 +585,11 @@ function createCognitiveRoutingProfileWizard(currentValue, models, allowInherit)
|
|
|
483
585
|
if (selectedValues.length === 1) {
|
|
484
586
|
const selected = selectedValues[0];
|
|
485
587
|
if (selected === LOCAL_INHERIT) {
|
|
486
|
-
return cognitiveRoutingConfirmStep(
|
|
588
|
+
return cognitiveRoutingConfirmStep(inheritChoice.summary);
|
|
487
589
|
}
|
|
488
590
|
const model = selected;
|
|
489
591
|
if (!model?.key || model.thinkingLevels.length === 0) return undefined;
|
|
490
|
-
const currentEffort =
|
|
491
|
-
currentProfile && currentModelKey === model.key ? currentProfile.thinkingLevel : undefined;
|
|
592
|
+
const currentEffort = currentProfile && currentModelKey === model.key ? currentProfile.thinking : undefined;
|
|
492
593
|
return {
|
|
493
594
|
title: `Choose effort · ${model.label}`,
|
|
494
595
|
choices: model.thinkingLevels.map((level) => ({
|
|
@@ -514,7 +615,7 @@ function createCognitiveRoutingProfileWizard(currentValue, models, allowInherit)
|
|
|
514
615
|
return {
|
|
515
616
|
provider: model.provider,
|
|
516
617
|
model: model.model,
|
|
517
|
-
|
|
618
|
+
thinking: selectedValues[1],
|
|
518
619
|
};
|
|
519
620
|
},
|
|
520
621
|
};
|
|
@@ -523,120 +624,71 @@ function cognitiveRoutingProfileItem(options) {
|
|
|
523
624
|
const path = ["cognitiveRouting", "profiles", options.name];
|
|
524
625
|
const repositoryProfile = getPath(options.rawConfig, path);
|
|
525
626
|
const localProfile = getPath(options.localConfig, path);
|
|
526
|
-
const
|
|
627
|
+
const configuredProfile = options.capabilityState?.profiles?.[options.name] ?? repositoryProfile;
|
|
628
|
+
const sessionPair = options.sessionProfiles?.[options.name];
|
|
629
|
+
const sessionProfile = sessionPair
|
|
630
|
+
? { provider: sessionPair.provider, model: sessionPair.modelId, thinking: sessionPair.thinking }
|
|
631
|
+
: undefined;
|
|
632
|
+
const effectiveProfile = sessionProfile ?? configuredProfile;
|
|
527
633
|
const localValue = isCognitiveRoutingProfile(localProfile) ? localProfile : LOCAL_INHERIT;
|
|
528
|
-
const value =
|
|
529
|
-
|
|
634
|
+
const value =
|
|
635
|
+
options.scope === "session"
|
|
636
|
+
? (sessionProfile ?? LOCAL_INHERIT)
|
|
637
|
+
: options.scope === "local"
|
|
638
|
+
? localValue
|
|
639
|
+
: repositoryProfile;
|
|
640
|
+
const configuredSource =
|
|
530
641
|
options.capabilityState?.profileSources?.[options.name] ??
|
|
531
642
|
(isCognitiveRoutingProfile(repositoryProfile) ? "repository" : undefined);
|
|
643
|
+
const source = options.scope === "session" ? (sessionProfile ? "session" : configuredSource) : configuredSource;
|
|
532
644
|
const models = cognitiveRoutingModelOptions(options.ctx);
|
|
533
|
-
const inactive = options.scope
|
|
645
|
+
const inactive = options.scope !== "repository" && models.length === 0 && value !== LOCAL_INHERIT;
|
|
646
|
+
const inheritChoice =
|
|
647
|
+
options.scope === "session"
|
|
648
|
+
? {
|
|
649
|
+
label: "Inherit configured preset",
|
|
650
|
+
description: "Remove the session override without changing local or repository configuration.",
|
|
651
|
+
summary: "inherit configured preset",
|
|
652
|
+
}
|
|
653
|
+
: undefined;
|
|
534
654
|
return {
|
|
535
655
|
id: `freeflow.cognitiveRouting.${options.name}`,
|
|
536
656
|
label: `${options.name.charAt(0).toUpperCase()}${options.name.slice(1)} preset`,
|
|
537
657
|
description:
|
|
538
|
-
|
|
658
|
+
options.scope === "session"
|
|
659
|
+
? "Temporarily choose the model and effort for this profile in the current Pi session only."
|
|
660
|
+
: "Choose an authenticated available model and one effort supported by that model. Confirming writes the complete preset; cancel leaves it unchanged.",
|
|
539
661
|
path,
|
|
540
662
|
kind: "string",
|
|
541
663
|
value,
|
|
542
|
-
format:
|
|
664
|
+
format: (current) => {
|
|
665
|
+
const inherits = current === LOCAL_INHERIT || (options.scope === "session" && current === undefined);
|
|
666
|
+
return inherits
|
|
667
|
+
? options.scope === "session"
|
|
668
|
+
? "inherit configured preset"
|
|
669
|
+
: "inherit repository preset"
|
|
670
|
+
: cognitiveRoutingProfileDisplay(current);
|
|
671
|
+
},
|
|
543
672
|
configScope: options.scope,
|
|
544
673
|
effectiveValue: effectiveProfile,
|
|
545
674
|
effectiveSource: cognitiveRoutingSettingsSource(source),
|
|
546
|
-
inheritedValue: repositoryProfile,
|
|
547
|
-
inheritedSource: "
|
|
675
|
+
inheritedValue: options.scope === "session" ? configuredProfile : repositoryProfile,
|
|
676
|
+
inheritedSource: cognitiveRoutingSettingsSource(configuredSource ?? "builtin"),
|
|
548
677
|
inactive,
|
|
549
678
|
displaySuffix: cognitiveRoutingProfileDisplaySuffix(source, options.scope),
|
|
550
|
-
wizard: () => createCognitiveRoutingProfileWizard(value, models, options.scope
|
|
679
|
+
wizard: () => createCognitiveRoutingProfileWizard(value, models, options.scope !== "repository", inheritChoice),
|
|
551
680
|
};
|
|
552
681
|
}
|
|
553
|
-
function cognitiveRoutingSessionStartItem(options) {
|
|
554
|
-
const path = ["cognitiveRouting", "sessionStart", options.setting];
|
|
555
|
-
const defaultValue = options.setting === "control" ? "automatic" : "reasoning";
|
|
556
|
-
const values = options.setting === "control" ? ["automatic", "manual"] : ["standard", "reasoning"];
|
|
557
|
-
const labels = Object.fromEntries(values.map((value) => [value, value]));
|
|
558
|
-
const descriptions =
|
|
559
|
-
options.setting === "control"
|
|
560
|
-
? {
|
|
561
|
-
automatic: "Start the new session in Reasoning; automatic delegation may use Standard during execution.",
|
|
562
|
-
manual: "Hold the selected profile for the new session until the user changes it.",
|
|
563
|
-
}
|
|
564
|
-
: {
|
|
565
|
-
standard: "Start a new manual session with the configured Standard preset.",
|
|
566
|
-
reasoning: "Start a new manual session with the configured Reasoning preset.",
|
|
567
|
-
};
|
|
568
|
-
const repositoryValue = getPath(options.rawConfig, path);
|
|
569
|
-
const localValue = getPath(options.localConfig, path);
|
|
570
|
-
const isValidValue = (value) => values.includes(value);
|
|
571
|
-
const repositorySetting = isValidValue(repositoryValue) ? repositoryValue : undefined;
|
|
572
|
-
const localSetting = isValidValue(localValue) ? localValue : undefined;
|
|
573
|
-
const inheritedValue = repositorySetting ?? defaultValue;
|
|
574
|
-
const inheritedSource = repositorySetting ? "repository" : "builtin";
|
|
575
|
-
const configuredSource =
|
|
576
|
-
options.capabilityState?.sessionStartSources?.[options.setting] ?? (repositorySetting ? "repository" : undefined);
|
|
577
|
-
const effectiveSessionStartControl =
|
|
578
|
-
options.capabilityState?.sessionStart?.control ??
|
|
579
|
-
getPath(options.localConfig, ["cognitiveRouting", "sessionStart", "control"]) ??
|
|
580
|
-
getPath(options.rawConfig, ["cognitiveRouting", "sessionStart", "control"]) ??
|
|
581
|
-
"automatic";
|
|
582
|
-
const effectiveValue =
|
|
583
|
-
options.setting === "profile" && effectiveSessionStartControl === "automatic"
|
|
584
|
-
? "reasoning"
|
|
585
|
-
: (options.capabilityState?.sessionStart?.[options.setting] ?? inheritedValue);
|
|
586
|
-
const effectiveSource = cognitiveRoutingSettingsSource(configuredSource);
|
|
587
|
-
const label = options.setting === "control" ? "Session start control" : "Manual session-start profile";
|
|
588
|
-
const description =
|
|
589
|
-
options.setting === "control"
|
|
590
|
-
? "Choose automatic or manual control for new sessions only; changing this does not alter the active session."
|
|
591
|
-
: "Choose the profile for manual session starts; automatic sessions always start in Reasoning.";
|
|
592
|
-
const item =
|
|
593
|
-
options.scope === "local"
|
|
594
|
-
? {
|
|
595
|
-
id: `freeflow.cognitiveRouting.sessionStart.${options.setting}`,
|
|
596
|
-
label,
|
|
597
|
-
description: `${description} Choose inherit to use the repository value; use /freeflow settings repo to edit shared defaults.`,
|
|
598
|
-
path,
|
|
599
|
-
kind: "enum",
|
|
600
|
-
value: localSetting ?? LOCAL_INHERIT,
|
|
601
|
-
values: [LOCAL_INHERIT, ...values],
|
|
602
|
-
valueLabels: { inherit: "Inherit repository", ...labels },
|
|
603
|
-
valueDescriptions: {
|
|
604
|
-
inherit: `Use ${inheritedValue} from ${inheritedSource}.`,
|
|
605
|
-
...descriptions,
|
|
606
|
-
},
|
|
607
|
-
format: (value) => String(value),
|
|
608
|
-
configScope: "local",
|
|
609
|
-
configValues: { inherit: undefined },
|
|
610
|
-
effectiveValue,
|
|
611
|
-
effectiveSource,
|
|
612
|
-
inheritedValue,
|
|
613
|
-
inheritedSource,
|
|
614
|
-
}
|
|
615
|
-
: {
|
|
616
|
-
id: `freeflow.cognitiveRouting.sessionStart.${options.setting}`,
|
|
617
|
-
label,
|
|
618
|
-
description: `${description} This edits shared .freeflow/config.json.`,
|
|
619
|
-
path,
|
|
620
|
-
kind: "enum",
|
|
621
|
-
value: inheritedValue,
|
|
622
|
-
values,
|
|
623
|
-
valueLabels: labels,
|
|
624
|
-
valueDescriptions: descriptions,
|
|
625
|
-
format: (value) => String(value),
|
|
626
|
-
defaultValue,
|
|
627
|
-
configScope: "repository",
|
|
628
|
-
effectiveValue,
|
|
629
|
-
effectiveSource,
|
|
630
|
-
localOverrideValue: localSetting,
|
|
631
|
-
};
|
|
632
|
-
item.displaySuffix = coreDisplaySuffix(item);
|
|
633
|
-
return item;
|
|
634
|
-
}
|
|
635
682
|
function isCognitiveRoutingRuntimeAvailable(pi) {
|
|
636
|
-
return
|
|
683
|
+
return (
|
|
684
|
+
!isPiFlowHost(pi?.host) &&
|
|
685
|
+
typeof pi?.appendEntry === "function" &&
|
|
686
|
+
typeof pi?.setModel === "function" &&
|
|
687
|
+
typeof pi?.setThinkingLevel === "function"
|
|
688
|
+
);
|
|
637
689
|
}
|
|
638
690
|
function freeflowItems(rawConfig, options = {}) {
|
|
639
|
-
const scope = options.scope
|
|
691
|
+
const scope = options.scope === "local" ? "local" : "repository";
|
|
640
692
|
const layers = options.layers;
|
|
641
693
|
const { localConfig, core, sources } = resolveSettingsCoreView(rawConfig, layers);
|
|
642
694
|
const freeflowItem = createScopedBooleanItem({
|
|
@@ -689,7 +741,7 @@ function freeflowItems(rawConfig, options = {}) {
|
|
|
689
741
|
label: "Enabled",
|
|
690
742
|
description: cognitiveRoutingRuntimeDisabled
|
|
691
743
|
? "Requires a host model-state control API; configuration is read-only on this host."
|
|
692
|
-
: "Allow Cognitive Routing to
|
|
744
|
+
: "Allow Cognitive Routing to manage explicit assignments and request native profile transitions.",
|
|
693
745
|
path: ["cognitiveRouting", "enabled"],
|
|
694
746
|
effectiveValue: cognitiveRoutingState?.enabled ?? false,
|
|
695
747
|
effectiveSource: cognitiveRoutingSettingsSource(cognitiveRoutingState?.enabledSource),
|
|
@@ -697,7 +749,31 @@ function freeflowItems(rawConfig, options = {}) {
|
|
|
697
749
|
});
|
|
698
750
|
cognitiveRoutingEnabledItem.inactive = freeflowInactive || cognitiveRoutingRuntimeDisabled;
|
|
699
751
|
cognitiveRoutingEnabledItem.runtimeInactive = cognitiveRoutingRuntimeDisabled;
|
|
700
|
-
const
|
|
752
|
+
const cognitiveRoutingDelegationItem = createScopedDelegationItem({
|
|
753
|
+
scope,
|
|
754
|
+
rawConfig,
|
|
755
|
+
localConfig,
|
|
756
|
+
effectiveValue: cognitiveRoutingState?.delegation ?? "executor",
|
|
757
|
+
effectiveSource: cognitiveRoutingSettingsSource(cognitiveRoutingState?.delegationSource),
|
|
758
|
+
});
|
|
759
|
+
cognitiveRoutingDelegationItem.inactive = freeflowInactive || cognitiveRoutingRuntimeDisabled;
|
|
760
|
+
cognitiveRoutingDelegationItem.runtimeInactive = cognitiveRoutingRuntimeDisabled;
|
|
761
|
+
const cognitiveRoutingProjectionItem = createScopedBooleanItem({
|
|
762
|
+
scope,
|
|
763
|
+
rawConfig,
|
|
764
|
+
localConfig,
|
|
765
|
+
id: "freeflow.cognitiveRouting.projection",
|
|
766
|
+
label: "Context projection",
|
|
767
|
+
description:
|
|
768
|
+
"Select worker evidence for Coordinator; disable projection to keep ordinary Pi context in all enabled profiles.",
|
|
769
|
+
path: ["cognitiveRouting", "projection"],
|
|
770
|
+
effectiveValue: cognitiveRoutingState?.projection ?? false,
|
|
771
|
+
effectiveSource: cognitiveRoutingSettingsSource(cognitiveRoutingState?.projectionSource),
|
|
772
|
+
defaultValue: false,
|
|
773
|
+
});
|
|
774
|
+
cognitiveRoutingProjectionItem.inactive = freeflowInactive || cognitiveRoutingRuntimeDisabled;
|
|
775
|
+
cognitiveRoutingProjectionItem.runtimeInactive = cognitiveRoutingRuntimeDisabled;
|
|
776
|
+
const cognitiveRoutingProfiles = ["coordinator", "helper", "executor"].map((name) =>
|
|
701
777
|
cognitiveRoutingProfileItem({
|
|
702
778
|
name: name,
|
|
703
779
|
scope,
|
|
@@ -707,16 +783,7 @@ function freeflowItems(rawConfig, options = {}) {
|
|
|
707
783
|
ctx: options.ctx,
|
|
708
784
|
}),
|
|
709
785
|
);
|
|
710
|
-
const
|
|
711
|
-
cognitiveRoutingSessionStartItem({
|
|
712
|
-
setting,
|
|
713
|
-
scope,
|
|
714
|
-
rawConfig,
|
|
715
|
-
localConfig,
|
|
716
|
-
capabilityState: cognitiveRoutingState,
|
|
717
|
-
}),
|
|
718
|
-
);
|
|
719
|
-
for (const item of [...cognitiveRoutingProfiles, ...cognitiveRoutingSessionStart]) {
|
|
786
|
+
for (const item of cognitiveRoutingProfiles) {
|
|
720
787
|
item.inactive ||= freeflowInactive || cognitiveRoutingRuntimeDisabled;
|
|
721
788
|
item.runtimeInactive = cognitiveRoutingRuntimeDisabled;
|
|
722
789
|
}
|
|
@@ -734,12 +801,17 @@ function freeflowItems(rawConfig, options = {}) {
|
|
|
734
801
|
label: "Cognitive Routing",
|
|
735
802
|
description: cognitiveRoutingRuntimeDisabled
|
|
736
803
|
? "Cognitive Routing configuration is visible for inspection but requires a host model-state control API."
|
|
737
|
-
: "Configure
|
|
804
|
+
: "Configure Coordinator, Helper, and Executor profiles plus the workers available for delegation.",
|
|
738
805
|
kind: "group",
|
|
739
806
|
value: cognitiveRoutingRuntimeDisabled ? false : (cognitiveRoutingState?.enabled ?? false),
|
|
740
807
|
inactive: freeflowInactive,
|
|
741
808
|
displaySuffix: cognitiveRoutingStatus,
|
|
742
|
-
children: [
|
|
809
|
+
children: [
|
|
810
|
+
cognitiveRoutingEnabledItem,
|
|
811
|
+
cognitiveRoutingDelegationItem,
|
|
812
|
+
...cognitiveRoutingProfiles,
|
|
813
|
+
cognitiveRoutingProjectionItem,
|
|
814
|
+
],
|
|
743
815
|
};
|
|
744
816
|
})();
|
|
745
817
|
return [
|
|
@@ -761,6 +833,7 @@ function pruneKnownDefaults(config) {
|
|
|
761
833
|
{ path: ["enabled"], value: DEFAULT_FREEFLOW_ENABLED },
|
|
762
834
|
{ path: ["contextVirtualization"], value: DEFAULT_CONTEXT_VIRTUALIZATION_ENABLED },
|
|
763
835
|
{ path: ["conversationHistory"], value: DEFAULT_CONVERSATION_HISTORY_ENABLED },
|
|
836
|
+
{ path: ["cognitiveRouting", "delegation"], value: "executor" },
|
|
764
837
|
];
|
|
765
838
|
for (const item of defaultPaths) {
|
|
766
839
|
if (valuesEqual(getPath(config, item.path), item.value)) {
|
|
@@ -834,7 +907,7 @@ async function updateConfig(cwd, item, value, scope = item.configScope ?? "repos
|
|
|
834
907
|
await writeFile(join(cwd, ".freeflow/config.json"), `${JSON.stringify(next, null, 2)}\n`, "utf8");
|
|
835
908
|
}
|
|
836
909
|
function isCognitiveRoutingProfileItem(item) {
|
|
837
|
-
return
|
|
910
|
+
return ["coordinator", "helper", "executor"].some((profile) => item.id === `freeflow.cognitiveRouting.${profile}`);
|
|
838
911
|
}
|
|
839
912
|
function valueForDisplay(item) {
|
|
840
913
|
let value;
|
|
@@ -874,10 +947,11 @@ function refreshSettingsDerivedState(items) {
|
|
|
874
947
|
const freeflowInactive = freeflowItem ? effectiveItemValue(freeflowItem) !== true : false;
|
|
875
948
|
const cognitiveRoutingGroup = findSettingsItem(items, "freeflow.cognitiveRouting");
|
|
876
949
|
const cognitiveRoutingEnabledItem = findSettingsItem(items, "freeflow.cognitiveRouting.enabled");
|
|
877
|
-
const
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
950
|
+
const cognitiveRoutingDelegationItem = findSettingsItem(items, "freeflow.cognitiveRouting.delegation");
|
|
951
|
+
const delegation = cognitiveRoutingDelegationItem ? effectiveItemValue(cognitiveRoutingDelegationItem) : "executor";
|
|
952
|
+
const cognitiveRoutingProfiles = ["coordinator", ...workersForDelegation(delegation)].map((profile) =>
|
|
953
|
+
findSettingsItem(items, `freeflow.cognitiveRouting.${profile}`),
|
|
954
|
+
);
|
|
881
955
|
if (cognitiveRoutingGroup && cognitiveRoutingEnabledItem) {
|
|
882
956
|
const enabled = effectiveItemValue(cognitiveRoutingEnabledItem) === true;
|
|
883
957
|
const profilesConfigured = cognitiveRoutingProfiles.every((item) =>
|
|
@@ -1152,7 +1226,7 @@ export async function handleFreeflowCommand(args, ctx, afterChange, pi, cognitiv
|
|
|
1152
1226
|
let reconcileCognitiveRouting = settingsScope === "session";
|
|
1153
1227
|
const items =
|
|
1154
1228
|
settingsScope === "session"
|
|
1155
|
-
? sessionFreeflowItems(state, cognitiveRoutingController)
|
|
1229
|
+
? sessionFreeflowItems(state, cognitiveRoutingController, ctx)
|
|
1156
1230
|
: freeflowItems(raw, {
|
|
1157
1231
|
scope: settingsScope,
|
|
1158
1232
|
layers,
|
|
@@ -1195,8 +1269,61 @@ export async function handleFreeflowCommand(args, ctx, afterChange, pi, cognitiv
|
|
|
1195
1269
|
return { changed: true, reloadRequired: false };
|
|
1196
1270
|
}
|
|
1197
1271
|
if (item.id === "freeflow.session.reset") {
|
|
1198
|
-
const
|
|
1199
|
-
|
|
1272
|
+
const routingResult = cognitiveRoutingController
|
|
1273
|
+
? await cognitiveRoutingController.resetSessionProfileOverrides("Reset session overrides")
|
|
1274
|
+
: { status: "unchanged" };
|
|
1275
|
+
if (routingResult.status === "blocked" || routingResult.reason) {
|
|
1276
|
+
ctx.ui.notify(
|
|
1277
|
+
`Cognitive Routing session presets could not be reset: ${routingResult.reason ?? routingResult.status}.`,
|
|
1278
|
+
"warning",
|
|
1279
|
+
);
|
|
1280
|
+
return { changed: false, reloadRequired: false };
|
|
1281
|
+
}
|
|
1282
|
+
try {
|
|
1283
|
+
const result = await resetSessionOverrides(ctx, pi);
|
|
1284
|
+
return {
|
|
1285
|
+
changed: result.changed || routingResult.status !== "unchanged",
|
|
1286
|
+
reloadRequired: result.reloadRequired,
|
|
1287
|
+
};
|
|
1288
|
+
} catch (error) {
|
|
1289
|
+
ctx.ui.notify(
|
|
1290
|
+
`Routing presets were reset, but core/context session overrides could not be reset: ${error instanceof Error ? error.message : String(error)}`,
|
|
1291
|
+
"warning",
|
|
1292
|
+
);
|
|
1293
|
+
return {
|
|
1294
|
+
changed: routingResult.status !== "unchanged",
|
|
1295
|
+
reloadRequired: false,
|
|
1296
|
+
};
|
|
1297
|
+
}
|
|
1298
|
+
}
|
|
1299
|
+
if (item.configScope === "session" && isCognitiveRoutingProfileItem(item)) {
|
|
1300
|
+
if (!cognitiveRoutingController) {
|
|
1301
|
+
ctx.ui.notify("Cognitive Routing is unavailable for this session.", "warning");
|
|
1302
|
+
return { changed: false, reloadRequired: false };
|
|
1303
|
+
}
|
|
1304
|
+
const profile = item.id.split(".").at(-1);
|
|
1305
|
+
const override =
|
|
1306
|
+
value === undefined || value === LOCAL_INHERIT
|
|
1307
|
+
? null
|
|
1308
|
+
: {
|
|
1309
|
+
provider: value.provider,
|
|
1310
|
+
modelId: value.model,
|
|
1311
|
+
thinking: value.thinking,
|
|
1312
|
+
};
|
|
1313
|
+
const result = await cognitiveRoutingController.setSessionProfileOverride(
|
|
1314
|
+
profile,
|
|
1315
|
+
override,
|
|
1316
|
+
"Session profile settings",
|
|
1317
|
+
);
|
|
1318
|
+
if (result.status === "blocked" || result.reason) {
|
|
1319
|
+
ctx.ui.notify(
|
|
1320
|
+
`Cognitive Routing session preset could not be applied: ${result.reason ?? result.status}.`,
|
|
1321
|
+
"warning",
|
|
1322
|
+
);
|
|
1323
|
+
return { changed: false, reloadRequired: false };
|
|
1324
|
+
}
|
|
1325
|
+
await afterChange(false);
|
|
1326
|
+
return { changed: result.status !== "unchanged", reloadRequired: false };
|
|
1200
1327
|
}
|
|
1201
1328
|
if (item.configScope === "session") {
|
|
1202
1329
|
const keyById = {
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
// A local planning estimate, not a provider token count or an output allocation.
|
|
2
|
+
// In particular, base64 and native usage/metadata are not model text.
|
|
3
|
+
export function estimateRequest(systemPrompt: string, tools: readonly any[], messages: readonly any[], model: any) {
|
|
4
|
+
const text = (value: string) => Math.ceil(value.length / 3);
|
|
5
|
+
const content = (value: any): number =>
|
|
6
|
+
typeof value === "string"
|
|
7
|
+
? text(value)
|
|
8
|
+
: Array.isArray(value)
|
|
9
|
+
? value.reduce(
|
|
10
|
+
(sum, b) =>
|
|
11
|
+
sum +
|
|
12
|
+
(b.type === "image"
|
|
13
|
+
? 1200
|
|
14
|
+
: b.type === "toolCall"
|
|
15
|
+
? text(b.name + JSON.stringify(b.arguments ?? {}))
|
|
16
|
+
: b.type === "thinking"
|
|
17
|
+
? text(b.thinking ?? "")
|
|
18
|
+
: text(b.text ?? "")),
|
|
19
|
+
0,
|
|
20
|
+
)
|
|
21
|
+
: 0;
|
|
22
|
+
const estimatedTokens =
|
|
23
|
+
text(systemPrompt) +
|
|
24
|
+
text(JSON.stringify(tools.map((t) => ({ name: t.name, description: t.description, parameters: t.parameters })))) +
|
|
25
|
+
messages.reduce((sum, m) => sum + 8 + content(m.content), 0);
|
|
26
|
+
const window = model?.contextWindow ?? 0;
|
|
27
|
+
const outputReserve = Math.min(4096, Math.max(1, Math.floor(window / 10)));
|
|
28
|
+
const maximumInputTokens = Math.max(0, window - outputReserve);
|
|
29
|
+
const estimateMethod =
|
|
30
|
+
"Planning estimate: text characters / 3, 1200 tokens/image, schemas and message framing; output headroom is not the provider allocation";
|
|
31
|
+
const warnings =
|
|
32
|
+
estimatedTokens > maximumInputTokens
|
|
33
|
+
? [
|
|
34
|
+
{
|
|
35
|
+
ref: "",
|
|
36
|
+
code: "budget_estimate",
|
|
37
|
+
detail: `Estimated input ${estimatedTokens} exceeds planning allowance ${maximumInputTokens}. Actual output allocation and image tokenization are provider-dependent; fit is unconfirmed. Use native compaction or revise explicitly if the provider rejects the request.`,
|
|
38
|
+
},
|
|
39
|
+
]
|
|
40
|
+
: [];
|
|
41
|
+
return { estimatedTokens, maximumInputTokens, outputReserve, estimateMethod, warnings };
|
|
42
|
+
}
|