@hassangameryt/freeflow 0.7.3 → 0.8.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.
- 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/NOTICE +14 -0
- package/README.md +26 -31
- package/capabilities/cognitive-routing/SKILL.md +106 -161
- package/capabilities/cognitive-routing/references/both-mode.md +36 -0
- package/capabilities/cognitive-routing/references/executor-mode.md +48 -0
- package/capabilities/cognitive-routing/references/helper-mode.md +47 -0
- package/capabilities/compaction/SKILL.md +49 -0
- package/capabilities/compaction/references/summary-format.md +239 -0
- package/command-surface.json +4 -0
- package/gemini-extension.json +1 -1
- package/hooks/shared/runtime-context.mjs +28 -30
- package/licenses/Apache-2.0.txt +55 -0
- package/package.json +22 -9
- package/pi-extension/README.md +41 -0
- package/pi-extension/dist/cognitive-routing/assembler.js +393 -0
- package/pi-extension/dist/{cognitive-routing-v2 → cognitive-routing}/config.js +5 -2
- package/pi-extension/dist/cognitive-routing/economics.js +45 -0
- package/pi-extension/dist/cognitive-routing/event-schema.js +388 -0
- package/pi-extension/dist/{session-sources/events.js → cognitive-routing/event-store.js} +103 -40
- package/pi-extension/dist/cognitive-routing/gate.js +241 -0
- package/pi-extension/dist/cognitive-routing/handlers.js +899 -0
- package/pi-extension/dist/cognitive-routing/handoffs.js +350 -0
- package/pi-extension/dist/cognitive-routing/model-control.js +467 -0
- package/pi-extension/dist/{cognitive-routing-v2 → cognitive-routing}/projection.js +229 -56
- package/pi-extension/dist/cognitive-routing/provenance.js +81 -0
- package/pi-extension/dist/cognitive-routing/runtime.js +396 -0
- package/pi-extension/dist/{cognitive-routing-v2 → cognitive-routing}/schemas.js +43 -7
- package/pi-extension/dist/cognitive-routing/session.js +221 -0
- package/pi-extension/dist/{session-sources → cognitive-routing}/sources.js +99 -85
- package/pi-extension/dist/{cognitive-routing-v2 → cognitive-routing}/state.js +37 -378
- package/pi-extension/dist/cognitive-routing/status.js +80 -0
- package/pi-extension/dist/{cognitive-routing-v2 → cognitive-routing}/tools.js +12 -12
- package/pi-extension/dist/{cognitive-routing-v2 → cognitive-routing}/types.js +5 -11
- package/pi-extension/dist/compaction/carry.js +191 -0
- package/pi-extension/dist/compaction/config.js +22 -0
- package/pi-extension/dist/compaction/controller.js +471 -0
- package/pi-extension/dist/compaction/harness.js +77 -0
- package/pi-extension/dist/compaction/results.js +158 -0
- package/pi-extension/dist/compaction/thresholds.js +35 -0
- package/pi-extension/dist/compaction/tool.js +57 -0
- package/pi-extension/dist/host/branch.js +13 -0
- package/pi-extension/dist/host/canonical.js +12 -0
- package/pi-extension/dist/host/catalog.js +77 -0
- package/pi-extension/dist/host/config.js +374 -0
- package/pi-extension/dist/host/projection-tags.js +60 -0
- package/pi-extension/dist/host/prompts.js +82 -0
- package/pi-extension/dist/{session-sources → host}/read-only-session.js +112 -1
- package/pi-extension/dist/{runtime → host}/request-history.js +121 -39
- package/pi-extension/dist/host/runtime-state.js +189 -0
- package/pi-extension/dist/{settings/settings-ui.js → host/settings/freeflow-command.js} +620 -410
- package/pi-extension/dist/{settings/settings-tui.js → host/settings/settings-view.js} +186 -73
- package/pi-extension/dist/host/staging.js +28 -0
- package/pi-extension/dist/host/status.js +101 -0
- package/pi-extension/dist/index.js +423 -173
- package/pi-extension/dist/provider-support/cache/anchor.js +21 -0
- package/pi-extension/dist/provider-support/cache/anthropic.js +102 -0
- package/pi-extension/dist/provider-support/cache/health.js +73 -0
- package/pi-extension/dist/provider-support/cache/index.js +58 -0
- package/pi-extension/dist/provider-support/cache/keepalive.js +210 -0
- package/pi-extension/dist/provider-support/cache/monitor.js +17 -0
- package/pi-extension/dist/provider-support/cache/openai-responses.js +48 -0
- package/pi-extension/dist/provider-support/effort.js +13 -0
- package/pi-extension/dist/provider-support/index.js +44 -3
- package/pi-extension/dist/provider-support/{astra → openai}/adapter.js +46 -29
- package/pi-extension/dist/provider-support/{astra → openai}/history.js +10 -8
- package/pi-extension/dist/provider-support/{astra → openai}/index.js +8 -4
- package/pi-extension/dist/provider-support/{astra → openai}/session-state.js +29 -38
- package/pi-extension/dist/provider-support/routes.js +17 -0
- package/pi-extension/dist/tool-execution/apply-patch/grammar.js +23 -0
- package/pi-extension/dist/tool-execution/apply-patch/match.js +38 -0
- package/pi-extension/dist/tool-execution/apply-patch/parser.js +177 -0
- package/pi-extension/dist/tool-execution/apply-patch/plan.js +201 -0
- package/pi-extension/dist/tool-execution/apply-patch/tool.js +84 -0
- package/pi-extension/dist/tool-execution/background.js +238 -0
- package/pi-extension/dist/tool-execution/bash-guard.js +23 -0
- package/pi-extension/dist/tool-execution/config.js +22 -0
- package/pi-extension/dist/tool-execution/edit-messages.js +120 -0
- package/pi-extension/dist/tool-execution/file-state.js +138 -0
- package/pi-extension/dist/tool-execution/file-tracking.js +143 -0
- package/pi-extension/dist/tool-execution/prompt.js +54 -0
- package/pi-extension/dist/tool-execution/tools.js +16 -0
- package/pi-extension/freeflow/README.md +9 -0
- package/plugin.json +1 -1
- package/runtime/prompts/cognitive-routing.md +4 -4
- package/runtime/prompts/core.md +22 -75
- package/runtime/prompts/interaction-contract.md +15 -8
- package/runtime/prompts/tool-execution.md +38 -0
- package/runtime/prompts/working-method.md +108 -0
- package/skills/action-selection/SKILL.md +39 -47
- package/skills/action-selection/references/trajectory-stalls.md +2 -0
- package/skills/bypass/SKILL.md +2 -2
- package/skills/commit-work/SKILL.md +31 -83
- package/skills/commit-work/references/git-state-edges.md +1 -1
- package/skills/commit-work/references/simple-push.md +2 -0
- package/skills/commit-work/references/staging-decisions.md +1 -1
- package/skills/decision-gate/SKILL.md +31 -50
- package/skills/design-for-depth/SKILL.md +44 -53
- package/skills/design-for-depth/references/design-pressure-signals.md +46 -144
- package/skills/design-for-depth/references/implementation-decisions.md +1 -1
- package/skills/design-for-depth/references/interface-design-loop.md +5 -36
- package/skills/design-for-depth/references/module-and-dependency-design.md +2 -32
- package/skills/design-for-depth/references/state-and-failure-boundaries.md +2 -13
- package/skills/diagnose-failure/SKILL.md +39 -24
- package/skills/diagnose-failure/references/diagnostic-loop-catalog.md +2 -0
- package/skills/diagnose-failure/references/flaky-and-performance-diagnosis.md +2 -2
- package/skills/discuss/SKILL.md +35 -11
- package/skills/discuss/references/discussion-continuity.md +7 -27
- package/skills/evaluate-skill/SKILL.md +49 -80
- package/skills/evaluate-skill/references/evaluation-definition-schema.md +35 -5
- package/skills/evaluate-skill/references/evaluation-design.md +14 -14
- package/skills/evaluate-skill/references/execution-and-evidence.md +26 -5
- package/skills/evaluate-skill/references/review-and-revision.md +3 -1
- package/skills/evaluate-skill/scripts/lib/definitions.mjs +49 -8
- package/skills/evaluate-skill/scripts/lib/pi.mjs +14 -7
- package/skills/evaluate-skill/scripts/lib/review.mjs +196 -0
- package/skills/evaluate-skill/scripts/lib/runner.mjs +45 -8
- package/skills/evaluate-skill/scripts/lib/sandbox.mjs +2 -0
- package/skills/evaluate-skill/scripts/lib/trials.mjs +103 -0
- package/skills/evaluate-skill/scripts/lib/view.mjs +67 -1
- package/skills/evaluate-skill/scripts/pi-command-tool.mjs +144 -0
- package/skills/evaluate-skill/scripts/pi-guard.mjs +30 -13
- package/skills/evaluate-skill/scripts/skill-eval.mjs +114 -34
- package/skills/execute-work/SKILL.md +41 -9
- package/skills/execute-work/references/code-practices.md +2 -0
- package/skills/execute-work/references/execute-work-edges.md +11 -1
- package/skills/handoff/SKILL.md +1 -1
- package/skills/handoff/references/handoff-templates.md +1 -1
- package/skills/review-artifact/SKILL.md +42 -41
- package/skills/review-artifact/references/adjudicate-artifact-review.md +1 -1
- package/skills/review-artifact/references/independent-artifact-reviewer-contract.md +1 -1
- package/skills/review-work/SKILL.md +38 -32
- package/skills/review-work/references/adjudicate-work-review.md +1 -1
- package/skills/review-work/references/independent-work-reviewer-contract.md +1 -1
- package/skills/review-work/references/security-risk-lens.md +2 -4
- package/skills/setup-freeflow/SKILL.md +3 -1
- package/skills/setup-freeflow/references/activation-contract.md +7 -13
- package/skills/setup-freeflow/references/host-setup.md +3 -3
- package/skills/track-work/SKILL.md +113 -161
- package/skills/track-work/references/working-record-format.md +189 -350
- package/skills/track-work/scripts/lib/document.mjs +62 -6
- package/skills/track-work/scripts/lib/format.mjs +16 -1
- package/skills/track-work/scripts/lib/store.mjs +9 -8
- package/skills/track-work/scripts/lib/transitions.mjs +79 -4
- package/skills/track-work/scripts/working-record.mjs +34 -12
- package/skills/verify-work/SKILL.md +29 -20
- package/skills/verify-work/references/browser-runtime-evidence.md +1 -5
- package/skills/verify-work/references/integration-evidence.md +2 -0
- package/skills/verify-work/references/performance-evidence.md +3 -1
- package/skills/verify-work/references/test-design.md +3 -1
- package/skills/workflow/SKILL.md +65 -21
- package/skills/workflow/references/domain-skill-composition.md +5 -3
- package/skills/write-docs/SKILL.md +83 -0
- package/skills/write-docs/references/documentation-shapes.md +119 -0
- package/skills/write-docs/scripts/docs-check.mjs +139 -0
- package/skills/write-plan/SKILL.md +40 -40
- package/skills/write-plan/references/plan-shapes.md +1 -1
- package/skills/write-skill/SKILL.md +107 -147
- package/skills/write-skill/references/activation-boundaries.md +2 -0
- package/skills/write-skill/references/agent-first-instructions.md +22 -10
- package/skills/write-skill/references/progressive-disclosure.md +4 -2
- package/skills/write-skill/references/skill-dependency-graphs.md +2 -0
- package/skills/write-skill/scripts/lib/similarity.mjs +56 -0
- package/skills/write-skill/scripts/skill-author.mjs +18 -2
- package/skills/write-spec/SKILL.md +34 -41
- package/skills/write-spec/references/artifact-standards.md +1 -1
- package/skills/write-spec/references/decision-records.md +2 -0
- package/skills/write-spec/references/spec-shapes.md +1 -1
- package/capabilities/context-virtualization/SKILL.md +0 -103
- package/capabilities/conversation-history/SKILL.md +0 -53
- package/hooks/tests/check-host-adapters.test.mjs +0 -82
- package/hooks/tests/check-runtime-context-hook.sh +0 -212
- package/pi-extension/dist/cognitive-routing-v2/runtime.js +0 -2181
- package/pi-extension/dist/context-virtualization/commands.js +0 -89
- package/pi-extension/dist/context-virtualization/projector.js +0 -42
- package/pi-extension/dist/context-virtualization/runtime.js +0 -378
- package/pi-extension/dist/context-virtualization/state.js +0 -57
- package/pi-extension/dist/context-virtualization/types.js +0 -10
- package/pi-extension/dist/conversation-history/passages.js +0 -66
- package/pi-extension/dist/conversation-history/ranking.js +0 -105
- package/pi-extension/dist/conversation-history/runtime.js +0 -226
- package/pi-extension/dist/conversation-history/search.js +0 -56
- package/pi-extension/dist/conversation-history/types.js +0 -1
- package/pi-extension/dist/freeflow-context/resolver.js +0 -120
- package/pi-extension/dist/freeflow-context/runtime.js +0 -80
- package/pi-extension/dist/freeflow-context/source-projector.js +0 -95
- package/pi-extension/dist/freeflow-context/tool.js +0 -314
- package/pi-extension/dist/freeflow-context/types.js +0 -12
- package/pi-extension/dist/freeflow-context/visibility.js +0 -28
- package/pi-extension/dist/integrations/mcp-config.js +0 -29
- package/pi-extension/dist/runtime/runtime-context.js +0 -754
- package/pi-extension/dist/runtime/runtime-identity.js +0 -5
- package/pi-extension/dist/session-sources/provenance.js +0 -32
- package/pi-extension/dist/ui/text-component.js +0 -57
- package/pi-extension/src/cognitive-routing-v2/budget.ts +0 -42
- package/pi-extension/src/cognitive-routing-v2/config.ts +0 -173
- package/pi-extension/src/cognitive-routing-v2/projection.ts +0 -375
- package/pi-extension/src/cognitive-routing-v2/render.ts +0 -277
- package/pi-extension/src/cognitive-routing-v2/runtime.ts +0 -2241
- package/pi-extension/src/cognitive-routing-v2/schemas.ts +0 -180
- package/pi-extension/src/cognitive-routing-v2/state.ts +0 -839
- package/pi-extension/src/cognitive-routing-v2/tools.ts +0 -62
- package/pi-extension/src/cognitive-routing-v2/types.ts +0 -254
- package/pi-extension/src/context-virtualization/commands.ts +0 -111
- package/pi-extension/src/context-virtualization/projector.ts +0 -62
- package/pi-extension/src/context-virtualization/runtime.ts +0 -428
- package/pi-extension/src/context-virtualization/state.ts +0 -79
- package/pi-extension/src/context-virtualization/types.ts +0 -92
- package/pi-extension/src/conversation-history/passages.ts +0 -81
- package/pi-extension/src/conversation-history/ranking.ts +0 -155
- package/pi-extension/src/conversation-history/runtime.ts +0 -250
- package/pi-extension/src/conversation-history/search.ts +0 -79
- package/pi-extension/src/conversation-history/types.ts +0 -31
- package/pi-extension/src/freeflow-context/resolver.ts +0 -139
- package/pi-extension/src/freeflow-context/runtime.ts +0 -100
- package/pi-extension/src/freeflow-context/source-projector.ts +0 -106
- package/pi-extension/src/freeflow-context/tool.ts +0 -366
- package/pi-extension/src/freeflow-context/types.ts +0 -70
- package/pi-extension/src/freeflow-context/visibility.ts +0 -53
- package/pi-extension/src/index.ts +0 -390
- package/pi-extension/src/integrations/mcp-config.ts +0 -32
- package/pi-extension/src/provider-support/astra/adapter.ts +0 -108
- package/pi-extension/src/provider-support/astra/history.ts +0 -145
- package/pi-extension/src/provider-support/astra/index.ts +0 -12
- package/pi-extension/src/provider-support/astra/session-state.ts +0 -113
- package/pi-extension/src/provider-support/index.ts +0 -5
- package/pi-extension/src/runtime/request-history.ts +0 -243
- package/pi-extension/src/runtime/runtime-context.ts +0 -860
- package/pi-extension/src/runtime/runtime-identity.ts +0 -8
- package/pi-extension/src/session-sources/events.ts +0 -230
- package/pi-extension/src/session-sources/provenance.ts +0 -39
- package/pi-extension/src/session-sources/read-only-session.ts +0 -170
- package/pi-extension/src/session-sources/sources.ts +0 -334
- package/pi-extension/src/settings/settings-tui.ts +0 -501
- package/pi-extension/src/settings/settings-ui.ts +0 -1652
- package/pi-extension/src/ui/text-component.ts +0 -66
- package/pi-extension/tests/cache-reuse/history.test.js +0 -117
- package/pi-extension/tests/cache-reuse/native.test.js +0 -91
- package/pi-extension/tests/cache-reuse/settings.test.js +0 -211
- package/pi-extension/tests/cognitive-routing-v2/communication.test.js +0 -577
- package/pi-extension/tests/cognitive-routing-v2/compaction-input.test.js +0 -183
- package/pi-extension/tests/cognitive-routing-v2/delegation.test.js +0 -557
- package/pi-extension/tests/cognitive-routing-v2/native.test.js +0 -474
- package/pi-extension/tests/cognitive-routing-v2/performance.test.js +0 -87
- package/pi-extension/tests/cognitive-routing-v2/persistence.test.js +0 -300
- package/pi-extension/tests/cognitive-routing-v2/projection.test.js +0 -241
- package/pi-extension/tests/cognitive-routing-v2/recovery.test.js +0 -1034
- package/pi-extension/tests/cognitive-routing-v2/render.test.js +0 -244
- package/pi-extension/tests/cognitive-routing-v2/runtime.test.js +0 -423
- package/pi-extension/tests/cognitive-routing-v2/stabilization.test.js +0 -521
- package/pi-extension/tests/cognitive-routing-v2/state.test.js +0 -586
- package/pi-extension/tests/context-virtualization/extension.test.js +0 -775
- package/pi-extension/tests/context-virtualization/projector.test.js +0 -110
- package/pi-extension/tests/context-virtualization/runtime.test.js +0 -230
- package/pi-extension/tests/conversation-history/runtime.test.js +0 -98
- package/pi-extension/tests/conversation-history/search.test.js +0 -97
- package/pi-extension/tests/fixtures/pi-host.js +0 -4
- package/pi-extension/tests/fixtures/routing-native.js +0 -191
- package/pi-extension/tests/integration/mode-free-core.test.js +0 -220
- package/pi-extension/tests/integration/pi-extension.test.js +0 -1447
- package/pi-extension/tests/integration/prompt-architecture.test.js +0 -447
- package/pi-extension/tests/integration/unaffected-regressions.test.js +0 -478
- package/pi-extension/tests/provider-support/astra-boundaries.test.js +0 -75
- package/pi-extension/tests/provider-support/astra.test.js +0 -306
- package/pi-extension/tests/provider-support/cli.test.js +0 -92
- package/pi-extension/tests/provider-support/native.test.js +0 -183
- package/pi-extension/tests/settings/settings-tui.test.js +0 -243
- package/pi-extension/tsconfig.json +0 -16
- package/runtime/prompts/context-virtualization.md +0 -3
- package/runtime/prompts/conversation-history.md +0 -3
- package/skills/design-for-depth/references/software-design-philosophy.md +0 -202
- /package/pi-extension/dist/{cognitive-routing-v2 → cognitive-routing}/budget.js +0 -0
- /package/pi-extension/dist/{cognitive-routing-v2 → cognitive-routing}/render.js +0 -0
|
@@ -9,14 +9,26 @@ import {
|
|
|
9
9
|
readFreeflowLocalConfig,
|
|
10
10
|
resetSessionOverrides,
|
|
11
11
|
setSessionCoreOverride,
|
|
12
|
-
} from "../
|
|
13
|
-
import {
|
|
14
|
-
import {
|
|
15
|
-
import {
|
|
12
|
+
} from "../config.js";
|
|
13
|
+
import { freeflowStatusText } from "../status.js";
|
|
14
|
+
import { PiSettingsComponent } from "./settings-view.js";
|
|
15
|
+
import { workersForDelegation } from "../../cognitive-routing/types.js";
|
|
16
|
+
import { DEFAULT_TOOL_EXECUTION_CONFIG } from "../../tool-execution/config.js";
|
|
17
|
+
import { DEFAULT_COMPACTION_CARRY, DEFAULT_COMPACTION_ENABLED } from "../../compaction/config.js";
|
|
18
|
+
/*
|
|
19
|
+
* The /freeflow command and the settings model it edits: every Freeflow setting with its repository, personal, and
|
|
20
|
+
* session scopes, the routing profile wizard, config file writes, and reload after saving. settings-view.ts draws the
|
|
21
|
+
* screen; this module decides what is on it.
|
|
22
|
+
*/
|
|
16
23
|
const DEFAULT_FREEFLOW_ENABLED = true;
|
|
17
|
-
const DEFAULT_CONTEXT_VIRTUALIZATION_ENABLED = false;
|
|
18
|
-
const DEFAULT_CONVERSATION_HISTORY_ENABLED = false;
|
|
19
24
|
const LOCAL_INHERIT = "inherit";
|
|
25
|
+
/** How a value's source reads in the settings screen. */
|
|
26
|
+
const SOURCE_LABELS = {
|
|
27
|
+
builtin: "default",
|
|
28
|
+
local: "personal",
|
|
29
|
+
repository: "repository",
|
|
30
|
+
session: "session",
|
|
31
|
+
};
|
|
20
32
|
const execFileAsync = promisify(execFile);
|
|
21
33
|
function isRecord(value) {
|
|
22
34
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
@@ -76,7 +88,7 @@ function setConfigValue(config, item, value) {
|
|
|
76
88
|
if (!item.path?.length) {
|
|
77
89
|
throw new Error(`${item.label} is a settings group, not a writable setting.`);
|
|
78
90
|
}
|
|
79
|
-
if (item.defaultValue !== undefined && valuesEqual(value, item.defaultValue)) {
|
|
91
|
+
if (value === undefined || (item.defaultValue !== undefined && valuesEqual(value, item.defaultValue))) {
|
|
80
92
|
deletePath(config, item.path);
|
|
81
93
|
return;
|
|
82
94
|
}
|
|
@@ -118,7 +130,8 @@ function coreDisplaySuffix(item, inactive = false) {
|
|
|
118
130
|
}
|
|
119
131
|
function updateScopedItemState(item, value) {
|
|
120
132
|
if (!item.configScope) return;
|
|
121
|
-
const
|
|
133
|
+
const selectedValue = configValueForChoice(item, value);
|
|
134
|
+
const configValue = selectedValue === undefined || isEmptyValue(selectedValue) ? undefined : selectedValue;
|
|
122
135
|
if (item.configScope === "session" || item.configScope === "local") {
|
|
123
136
|
item.effectiveValue = configValue === undefined ? item.inheritedValue : configValue;
|
|
124
137
|
item.effectiveSource = configValue === undefined ? (item.inheritedSource ?? "builtin") : item.configScope;
|
|
@@ -144,20 +157,16 @@ function createScopedBooleanItem(options) {
|
|
|
144
157
|
item = {
|
|
145
158
|
id: options.id,
|
|
146
159
|
label: options.label,
|
|
147
|
-
description: `${options.description}
|
|
160
|
+
description: `${options.description} Inherit uses the repository value.`,
|
|
148
161
|
path: options.path,
|
|
149
162
|
kind: "enum",
|
|
150
163
|
value: selectedValue,
|
|
151
164
|
values: [LOCAL_INHERIT, "true", "false"],
|
|
152
|
-
valueLabels: {
|
|
153
|
-
inherit: "Inherit repository",
|
|
154
|
-
true: "enabled",
|
|
155
|
-
false: "disabled",
|
|
156
|
-
},
|
|
165
|
+
valueLabels: { inherit: "inherit", true: "enabled", false: "disabled" },
|
|
157
166
|
valueDescriptions: {
|
|
158
|
-
inherit: `Use ${formatCoreValue(inheritedValue)} from ${inheritedSource}.`,
|
|
159
|
-
true: "
|
|
160
|
-
false: "
|
|
167
|
+
inherit: `Use ${formatCoreValue(inheritedValue)} from ${SOURCE_LABELS[inheritedSource]}.`,
|
|
168
|
+
true: "Enabled for you in this checkout.",
|
|
169
|
+
false: "Disabled for you in this checkout.",
|
|
161
170
|
},
|
|
162
171
|
format: (value) => {
|
|
163
172
|
if (value === LOCAL_INHERIT) return LOCAL_INHERIT;
|
|
@@ -178,7 +187,7 @@ function createScopedBooleanItem(options) {
|
|
|
178
187
|
item = {
|
|
179
188
|
id: options.id,
|
|
180
189
|
label: options.label,
|
|
181
|
-
description:
|
|
190
|
+
description: options.description,
|
|
182
191
|
path: options.path,
|
|
183
192
|
kind: "boolean",
|
|
184
193
|
value: inheritedValue,
|
|
@@ -207,7 +216,7 @@ function createScopedDelegationItem(options) {
|
|
|
207
216
|
options.scope === "local"
|
|
208
217
|
? {
|
|
209
218
|
id: "freeflow.cognitiveRouting.delegation",
|
|
210
|
-
label: "Delegation
|
|
219
|
+
label: "Delegation",
|
|
211
220
|
description:
|
|
212
221
|
"Choose enabled workers for automatic routing. Inherit uses the repository value; this setting does not rank model presets.",
|
|
213
222
|
path,
|
|
@@ -215,13 +224,13 @@ function createScopedDelegationItem(options) {
|
|
|
215
224
|
value: ["executor", "helper", "both"].includes(localValue) ? localValue : LOCAL_INHERIT,
|
|
216
225
|
values: [LOCAL_INHERIT, "executor", "helper", "both"],
|
|
217
226
|
valueLabels: {
|
|
218
|
-
inherit: "
|
|
227
|
+
inherit: "inherit",
|
|
219
228
|
executor: "Executor only",
|
|
220
229
|
helper: "Helper only",
|
|
221
230
|
both: "Helper and Executor",
|
|
222
231
|
},
|
|
223
232
|
valueDescriptions: {
|
|
224
|
-
inherit: `Use ${inheritedValue} from ${inheritedSource}.`,
|
|
233
|
+
inherit: `Use ${inheritedValue} from ${SOURCE_LABELS[inheritedSource]}.`,
|
|
225
234
|
...descriptions,
|
|
226
235
|
},
|
|
227
236
|
configScope: "local",
|
|
@@ -233,7 +242,7 @@ function createScopedDelegationItem(options) {
|
|
|
233
242
|
}
|
|
234
243
|
: {
|
|
235
244
|
id: "freeflow.cognitiveRouting.delegation",
|
|
236
|
-
label: "Delegation
|
|
245
|
+
label: "Delegation",
|
|
237
246
|
description: "Choose enabled workers for automatic routing. This edits shared .freeflow/config.json.",
|
|
238
247
|
path,
|
|
239
248
|
kind: "enum",
|
|
@@ -256,16 +265,9 @@ function createScopedDelegationItem(options) {
|
|
|
256
265
|
}
|
|
257
266
|
function resolveSettingsCoreView(rawConfig, layers) {
|
|
258
267
|
const localConfig = layers?.local.valid && isRecord(layers.local.parsed) ? layers.local.parsed : {};
|
|
259
|
-
const fallbackCore = {
|
|
260
|
-
enabled: getPath(rawConfig, ["enabled"]) !== false,
|
|
261
|
-
contextVirtualization: getPath(rawConfig, ["contextVirtualization"]) === true,
|
|
262
|
-
conversationHistory: getPath(rawConfig, ["conversationHistory"]) === true,
|
|
263
|
-
};
|
|
268
|
+
const fallbackCore = { enabled: getPath(rawConfig, ["enabled"]) !== false };
|
|
264
269
|
const fallbackSources = {
|
|
265
270
|
enabled: typeof getPath(rawConfig, ["enabled"]) === "boolean" ? "repository" : "builtin",
|
|
266
|
-
contextVirtualization:
|
|
267
|
-
typeof getPath(rawConfig, ["contextVirtualization"]) === "boolean" ? "repository" : "builtin",
|
|
268
|
-
conversationHistory: typeof getPath(rawConfig, ["conversationHistory"]) === "boolean" ? "repository" : "builtin",
|
|
269
271
|
};
|
|
270
272
|
return {
|
|
271
273
|
localConfig,
|
|
@@ -275,166 +277,258 @@ function resolveSettingsCoreView(rawConfig, layers) {
|
|
|
275
277
|
}
|
|
276
278
|
function createSessionBooleanItem(options) {
|
|
277
279
|
const override = options.sessionOverrides[options.key];
|
|
278
|
-
|
|
280
|
+
return {
|
|
279
281
|
id: options.id,
|
|
280
282
|
label: options.label,
|
|
281
|
-
description: options.description
|
|
283
|
+
description: `${options.description} Inherit uses the personal or repository value.`,
|
|
284
|
+
path: options.key.split("."),
|
|
282
285
|
kind: "enum",
|
|
283
286
|
value: typeof override === "boolean" ? String(override) : LOCAL_INHERIT,
|
|
284
287
|
values: [LOCAL_INHERIT, "true", "false"],
|
|
285
|
-
valueLabels: {
|
|
286
|
-
inherit: "Inherit configured value",
|
|
287
|
-
true: "Enabled for this session",
|
|
288
|
-
false: "Disabled for this session",
|
|
289
|
-
},
|
|
288
|
+
valueLabels: { inherit: "inherit", true: "enabled", false: "disabled" },
|
|
290
289
|
valueDescriptions: {
|
|
291
|
-
inherit: `Use ${formatCoreValue(options.inheritedValue)} from ${options.inheritedSource}.`,
|
|
292
|
-
true: "
|
|
293
|
-
false: "
|
|
294
|
-
},
|
|
295
|
-
format: (value) => {
|
|
296
|
-
if (value === LOCAL_INHERIT) return LOCAL_INHERIT;
|
|
297
|
-
return booleanValue(value === "true");
|
|
290
|
+
inherit: `Use ${formatCoreValue(options.inheritedValue)} from ${SOURCE_LABELS[options.inheritedSource]}.`,
|
|
291
|
+
true: "Enabled for this Pi session only.",
|
|
292
|
+
false: "Disabled for this Pi session only.",
|
|
298
293
|
},
|
|
299
294
|
configScope: "session",
|
|
300
|
-
configValues: {
|
|
301
|
-
inherit: undefined,
|
|
302
|
-
true: true,
|
|
303
|
-
false: false,
|
|
304
|
-
},
|
|
295
|
+
configValues: { inherit: undefined, true: true, false: false },
|
|
305
296
|
effectiveValue: options.effectiveValue,
|
|
306
|
-
effectiveSource: options.
|
|
297
|
+
effectiveSource: typeof override === "boolean" ? "session" : options.inheritedSource,
|
|
307
298
|
inheritedValue: options.inheritedValue,
|
|
308
299
|
inheritedSource: options.inheritedSource,
|
|
309
300
|
};
|
|
310
|
-
item.displaySuffix = coreDisplaySuffix(item);
|
|
311
|
-
return item;
|
|
312
301
|
}
|
|
313
|
-
|
|
302
|
+
/** A setting's configured value and source without session overrides: personal, then repository, then the default. */
|
|
303
|
+
function configuredSetting(rawConfig, localConfig, path, defaultValue) {
|
|
304
|
+
const local = getPath(localConfig, path);
|
|
305
|
+
if (typeof local === "boolean") return { value: local, source: "local" };
|
|
306
|
+
const repository = getPath(rawConfig, path);
|
|
307
|
+
if (typeof repository === "boolean") return { value: repository, source: "repository" };
|
|
308
|
+
return { value: defaultValue, source: "builtin" };
|
|
309
|
+
}
|
|
310
|
+
function sectionItem(id, label, description, status, children) {
|
|
311
|
+
return { id, label, description, kind: "group", value: true, displaySuffix: status, children };
|
|
312
|
+
}
|
|
313
|
+
function capabilityStatus(state) {
|
|
314
|
+
return state?.effective ? "active" : state?.enabled ? "inactive" : "disabled";
|
|
315
|
+
}
|
|
316
|
+
function sessionFreeflowItems(state, cognitiveRoutingController, ctx, rawConfig, localConfig) {
|
|
314
317
|
const sessionOverrides = state.sessionOverrides;
|
|
315
318
|
const configured = state.configuredCoreConfig;
|
|
316
319
|
const configuredSources = state.configuredSources;
|
|
317
|
-
const
|
|
320
|
+
const freeflowInactive = !state.enabled;
|
|
321
|
+
const sessionSwitch = (id, label, description, key, effective, fallback) => {
|
|
322
|
+
const inherited = configuredSetting(rawConfig, localConfig, key.split("."), fallback);
|
|
323
|
+
const item = createSessionBooleanItem({
|
|
324
|
+
id,
|
|
325
|
+
label,
|
|
326
|
+
description,
|
|
327
|
+
key,
|
|
328
|
+
inheritedValue: inherited.value,
|
|
329
|
+
inheritedSource: inherited.source,
|
|
330
|
+
effectiveValue: effective,
|
|
331
|
+
sessionOverrides,
|
|
332
|
+
});
|
|
333
|
+
item.inactive = freeflowInactive;
|
|
334
|
+
return item;
|
|
335
|
+
};
|
|
318
336
|
const freeflowItem = createSessionBooleanItem({
|
|
319
337
|
id: "freeflow.enabled",
|
|
320
338
|
label: "Freeflow",
|
|
321
|
-
description: "
|
|
339
|
+
description: "Master switch for Freeflow in this Pi session.",
|
|
322
340
|
key: "enabled",
|
|
323
341
|
inheritedValue: configured.enabled,
|
|
324
342
|
inheritedSource: configuredSources.enabled,
|
|
325
343
|
effectiveValue: state.enabled,
|
|
326
|
-
effectiveSource: effectiveSources.enabled,
|
|
327
|
-
sessionOverrides,
|
|
328
|
-
});
|
|
329
|
-
const contextVirtualizationItem = createSessionBooleanItem({
|
|
330
|
-
id: "freeflow.contextVirtualization",
|
|
331
|
-
label: "Context Virtualization",
|
|
332
|
-
description: "Temporary Context Virtualization override for this Pi session.",
|
|
333
|
-
key: "contextVirtualization",
|
|
334
|
-
inheritedValue: configured.contextVirtualization,
|
|
335
|
-
inheritedSource: configuredSources.contextVirtualization,
|
|
336
|
-
effectiveValue: state.contextVirtualization.enabled,
|
|
337
|
-
effectiveSource: effectiveSources.contextVirtualization,
|
|
338
|
-
sessionOverrides,
|
|
339
|
-
});
|
|
340
|
-
const conversationHistoryItem = createSessionBooleanItem({
|
|
341
|
-
id: "freeflow.conversationHistory",
|
|
342
|
-
label: "Conversation History",
|
|
343
|
-
description: "Temporary Conversation History override for this Pi session.",
|
|
344
|
-
key: "conversationHistory",
|
|
345
|
-
inheritedValue: configured.conversationHistory,
|
|
346
|
-
inheritedSource: configuredSources.conversationHistory,
|
|
347
|
-
effectiveValue: state.conversationHistory.enabled,
|
|
348
|
-
effectiveSource: effectiveSources.conversationHistory,
|
|
349
344
|
sessionOverrides,
|
|
350
345
|
});
|
|
351
|
-
const
|
|
352
|
-
|
|
353
|
-
conversationHistoryItem.inactive = freeflowInactive;
|
|
346
|
+
const routing = state.cognitiveRouting;
|
|
347
|
+
const routingUnavailable = cognitiveRoutingController === undefined;
|
|
354
348
|
const cognitiveRoutingState = cognitiveRoutingController?.state();
|
|
355
|
-
const
|
|
349
|
+
const manualProfiles = ["coordinator", "helper", "executor"];
|
|
356
350
|
const heldProfile = cognitiveRoutingState?.controlMode.startsWith("manual-")
|
|
357
351
|
? cognitiveRoutingState.activeProfile
|
|
358
352
|
: undefined;
|
|
359
|
-
const
|
|
360
|
-
|
|
361
|
-
const
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
353
|
+
const control = heldProfile && manualProfiles.includes(heldProfile) ? heldProfile : "auto";
|
|
354
|
+
const routingInactive = freeflowInactive || !routing?.effective || routingUnavailable;
|
|
355
|
+
const routingItems = [];
|
|
356
|
+
if (routing) {
|
|
357
|
+
const enabledItem = sessionSwitch(
|
|
358
|
+
"freeflow.cognitiveRouting.enabled",
|
|
359
|
+
"Enabled",
|
|
360
|
+
"Use Cognitive Routing in this session.",
|
|
361
|
+
"cognitiveRouting.enabled",
|
|
362
|
+
routing.enabled,
|
|
363
|
+
false,
|
|
364
|
+
);
|
|
365
|
+
enabledItem.inactive = freeflowInactive || routingUnavailable;
|
|
366
|
+
const projectionItem = sessionSwitch(
|
|
367
|
+
"freeflow.cognitiveRouting.projection",
|
|
368
|
+
"Context projection",
|
|
369
|
+
"Show Coordinator the worker evidence selected for it instead of the whole history.",
|
|
370
|
+
"cognitiveRouting.projection",
|
|
371
|
+
routing.projection,
|
|
372
|
+
false,
|
|
373
|
+
);
|
|
374
|
+
projectionItem.inactive = routingInactive;
|
|
375
|
+
const delegationOverride = cognitiveRoutingController?.sessionDelegationOverride() ?? LOCAL_INHERIT;
|
|
376
|
+
const delegation = cognitiveRoutingState?.delegation ?? routing.delegation;
|
|
377
|
+
routingItems.push(
|
|
378
|
+
enabledItem,
|
|
379
|
+
{
|
|
365
380
|
id: "freeflow.cognitiveRouting.profile",
|
|
366
|
-
label: "
|
|
367
|
-
description: "
|
|
381
|
+
label: "Control",
|
|
382
|
+
description: "Automatic routing, or hold one profile until you release it.",
|
|
368
383
|
kind: "enum",
|
|
369
|
-
value:
|
|
384
|
+
value: control,
|
|
370
385
|
values: ["auto", ...manualProfiles],
|
|
371
386
|
valueLabels: Object.fromEntries([
|
|
372
387
|
["auto", "automatic"],
|
|
373
|
-
...manualProfiles.map((profile) => [profile, `
|
|
388
|
+
...manualProfiles.map((profile) => [profile, `hold ${profile}`]),
|
|
374
389
|
]),
|
|
375
390
|
valueDescriptions: Object.fromEntries([
|
|
376
|
-
["auto", "Release
|
|
377
|
-
...manualProfiles.map((profile) => [
|
|
391
|
+
["auto", "Release any hold; Coordinator places the work."],
|
|
392
|
+
...manualProfiles.map((profile) => [
|
|
393
|
+
profile,
|
|
394
|
+
`Hold ${profile} when delegation enables it; release with /freeflow profile auto.`,
|
|
395
|
+
]),
|
|
378
396
|
]),
|
|
379
|
-
inactive:
|
|
380
|
-
runtimeInactive:
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
})
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
inactive:
|
|
408
|
-
|
|
397
|
+
inactive: routingInactive,
|
|
398
|
+
runtimeInactive: routing.blockingReason?.code === "runtime_disabled",
|
|
399
|
+
},
|
|
400
|
+
{
|
|
401
|
+
id: "freeflow.cognitiveRouting.delegation",
|
|
402
|
+
label: "Delegation",
|
|
403
|
+
description: "Which workers Coordinator may use in this session.",
|
|
404
|
+
kind: "enum",
|
|
405
|
+
value: delegationOverride,
|
|
406
|
+
values: [LOCAL_INHERIT, "executor", "helper", "both"],
|
|
407
|
+
valueLabels: {
|
|
408
|
+
inherit: "inherit",
|
|
409
|
+
executor: "Executor only",
|
|
410
|
+
helper: "Helper only",
|
|
411
|
+
both: "Helper and Executor",
|
|
412
|
+
},
|
|
413
|
+
valueDescriptions: {
|
|
414
|
+
inherit: `Use ${routing.delegation} from ${SOURCE_LABELS[cognitiveRoutingSettingsSource(routing.delegationSource)]}.`,
|
|
415
|
+
executor: "Coordinator delegates assignments to Executor.",
|
|
416
|
+
helper: "Coordinator delegates supporting assignments to Helper and keeps production implementation.",
|
|
417
|
+
both: "Coordinator chooses Helper or Executor for each assignment.",
|
|
418
|
+
},
|
|
419
|
+
configScope: "session",
|
|
420
|
+
effectiveValue: delegation,
|
|
421
|
+
effectiveSource:
|
|
422
|
+
delegationOverride === LOCAL_INHERIT ? cognitiveRoutingSettingsSource(routing.delegationSource) : "session",
|
|
423
|
+
inheritedValue: routing.delegation,
|
|
424
|
+
inheritedSource: cognitiveRoutingSettingsSource(routing.delegationSource),
|
|
425
|
+
inactive: routingInactive,
|
|
426
|
+
},
|
|
427
|
+
projectionItem,
|
|
428
|
+
);
|
|
429
|
+
if (cognitiveRoutingController) {
|
|
430
|
+
const sessionProfiles = cognitiveRoutingController.sessionProfileOverrides();
|
|
431
|
+
for (const name of manualProfiles) {
|
|
432
|
+
const item = cognitiveRoutingProfileItem({
|
|
433
|
+
name,
|
|
434
|
+
scope: "session",
|
|
435
|
+
rawConfig: {},
|
|
436
|
+
localConfig: {},
|
|
437
|
+
capabilityState: routing,
|
|
438
|
+
ctx,
|
|
439
|
+
sessionProfiles,
|
|
440
|
+
});
|
|
441
|
+
item.inactive ||= routingInactive;
|
|
442
|
+
routingItems.push(item);
|
|
409
443
|
}
|
|
410
|
-
|
|
411
|
-
|
|
444
|
+
}
|
|
445
|
+
}
|
|
446
|
+
const toolItem = sessionSwitch(
|
|
447
|
+
"freeflow.toolExecution.enabled",
|
|
448
|
+
"Enabled",
|
|
449
|
+
"Use Tool Execution in this session.",
|
|
450
|
+
"toolExecution.enabled",
|
|
451
|
+
state.toolExecution?.enabled ?? false,
|
|
452
|
+
DEFAULT_TOOL_EXECUTION_CONFIG.enabled,
|
|
453
|
+
);
|
|
454
|
+
const compactionItem = sessionSwitch(
|
|
455
|
+
"freeflow.compaction.enabled",
|
|
456
|
+
"Enabled",
|
|
457
|
+
"Use Freeflow compaction in this session.",
|
|
458
|
+
"compaction.enabled",
|
|
459
|
+
state.compaction?.enabled ?? DEFAULT_COMPACTION_ENABLED,
|
|
460
|
+
DEFAULT_COMPACTION_ENABLED,
|
|
461
|
+
);
|
|
462
|
+
const carryItem = sessionSwitch(
|
|
463
|
+
"freeflow.compaction.carry",
|
|
464
|
+
"Context reuse",
|
|
465
|
+
"Let the agent carry selected files and tool results into the next cycle.",
|
|
466
|
+
"compaction.carry",
|
|
467
|
+
state.compaction?.carry ?? DEFAULT_COMPACTION_CARRY,
|
|
468
|
+
DEFAULT_COMPACTION_CARRY,
|
|
469
|
+
);
|
|
470
|
+
const items = [
|
|
412
471
|
freeflowItem,
|
|
413
|
-
...(
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
472
|
+
...(routing
|
|
473
|
+
? [
|
|
474
|
+
sectionItem(
|
|
475
|
+
"freeflow.cognitiveRouting",
|
|
476
|
+
"Cognitive Routing",
|
|
477
|
+
"Routing for this session.",
|
|
478
|
+
routingUnavailable
|
|
479
|
+
? "unavailable"
|
|
480
|
+
: cognitiveRoutingState?.effective
|
|
481
|
+
? `active · ${control === "auto" ? "automatic" : `hold ${control}`}`
|
|
482
|
+
: capabilityStatus(routing),
|
|
483
|
+
routingItems,
|
|
484
|
+
),
|
|
485
|
+
]
|
|
486
|
+
: []),
|
|
487
|
+
sectionItem(
|
|
488
|
+
"freeflow.toolExecution",
|
|
489
|
+
"Tool Execution",
|
|
490
|
+
"Tool Execution for this session.",
|
|
491
|
+
capabilityStatus(state.toolExecution),
|
|
492
|
+
[toolItem],
|
|
493
|
+
),
|
|
494
|
+
sectionItem(
|
|
495
|
+
"freeflow.compaction",
|
|
496
|
+
"Compaction",
|
|
497
|
+
"Compaction for this session.",
|
|
498
|
+
capabilityStatus(state.compaction),
|
|
499
|
+
[compactionItem, carryItem],
|
|
500
|
+
),
|
|
501
|
+
sectionItem("freeflow.session", "Session", "Session overrides.", "", [
|
|
502
|
+
{
|
|
503
|
+
id: "freeflow.session.reset",
|
|
504
|
+
label: "Reset overrides",
|
|
505
|
+
description:
|
|
506
|
+
"Clear every session override above, so the session follows personal and repository settings again.",
|
|
507
|
+
kind: "enum",
|
|
508
|
+
value: "available",
|
|
509
|
+
values: ["reset"],
|
|
510
|
+
valueLabels: { reset: "Reset all session overrides" },
|
|
511
|
+
valueDescriptions: {
|
|
512
|
+
reset: "Return every session setting, delegation, and routing preset to its configured value.",
|
|
513
|
+
},
|
|
514
|
+
// Counted when shown, so it follows changes made on this screen.
|
|
515
|
+
format: () => {
|
|
516
|
+
let count = 0;
|
|
517
|
+
walkSettingsItems(items, (candidate) => {
|
|
518
|
+
if (
|
|
519
|
+
candidate.configScope === "session" &&
|
|
520
|
+
candidate.value !== LOCAL_INHERIT &&
|
|
521
|
+
candidate.value !== undefined
|
|
522
|
+
)
|
|
523
|
+
count += 1;
|
|
524
|
+
});
|
|
525
|
+
return count ? `${count} active` : "none active";
|
|
526
|
+
},
|
|
527
|
+
transient: true,
|
|
528
|
+
},
|
|
529
|
+
]),
|
|
437
530
|
];
|
|
531
|
+
return items;
|
|
438
532
|
}
|
|
439
533
|
const COGNITIVE_ROUTING_THINKING_LEVELS = ["off", "minimal", "low", "medium", "high", "xhigh", "max"];
|
|
440
534
|
const COGNITIVE_ROUTING_EFFORT_DESCRIPTIONS = {
|
|
@@ -633,7 +727,7 @@ function cognitiveRoutingProfileItem(options) {
|
|
|
633
727
|
const localValue = isCognitiveRoutingProfile(localProfile) ? localProfile : LOCAL_INHERIT;
|
|
634
728
|
const value =
|
|
635
729
|
options.scope === "session"
|
|
636
|
-
? (sessionProfile ?? LOCAL_INHERIT)
|
|
730
|
+
? (sessionProfile ?? (configuredProfile ? LOCAL_INHERIT : undefined))
|
|
637
731
|
: options.scope === "local"
|
|
638
732
|
? localValue
|
|
639
733
|
: repositoryProfile;
|
|
@@ -665,7 +759,9 @@ function cognitiveRoutingProfileItem(options) {
|
|
|
665
759
|
const inherits = current === LOCAL_INHERIT || (options.scope === "session" && current === undefined);
|
|
666
760
|
return inherits
|
|
667
761
|
? options.scope === "session"
|
|
668
|
-
?
|
|
762
|
+
? configuredProfile
|
|
763
|
+
? "inherit configured preset"
|
|
764
|
+
: "not configured"
|
|
669
765
|
: "inherit repository preset"
|
|
670
766
|
: cognitiveRoutingProfileDisplay(current);
|
|
671
767
|
},
|
|
@@ -676,12 +772,17 @@ function cognitiveRoutingProfileItem(options) {
|
|
|
676
772
|
inheritedSource: cognitiveRoutingSettingsSource(configuredSource ?? "builtin"),
|
|
677
773
|
inactive,
|
|
678
774
|
displaySuffix: cognitiveRoutingProfileDisplaySuffix(source, options.scope),
|
|
679
|
-
wizard: () =>
|
|
775
|
+
wizard: () =>
|
|
776
|
+
createCognitiveRoutingProfileWizard(
|
|
777
|
+
value,
|
|
778
|
+
models,
|
|
779
|
+
options.scope !== "repository" && (options.scope !== "session" || configuredProfile !== undefined),
|
|
780
|
+
inheritChoice,
|
|
781
|
+
),
|
|
680
782
|
};
|
|
681
783
|
}
|
|
682
784
|
function isCognitiveRoutingRuntimeAvailable(pi) {
|
|
683
785
|
return (
|
|
684
|
-
!isPiFlowHost(pi?.host) &&
|
|
685
786
|
typeof pi?.appendEntry === "function" &&
|
|
686
787
|
typeof pi?.setModel === "function" &&
|
|
687
788
|
typeof pi?.setThinkingLevel === "function"
|
|
@@ -703,33 +804,7 @@ function freeflowItems(rawConfig, options = {}) {
|
|
|
703
804
|
effectiveSource: sources.enabled,
|
|
704
805
|
defaultValue: DEFAULT_FREEFLOW_ENABLED,
|
|
705
806
|
});
|
|
706
|
-
const contextVirtualizationItem = createScopedBooleanItem({
|
|
707
|
-
scope,
|
|
708
|
-
rawConfig,
|
|
709
|
-
localConfig,
|
|
710
|
-
id: "freeflow.contextVirtualization",
|
|
711
|
-
label: "Context Virtualization",
|
|
712
|
-
description: "Let the model archive consumed tool results from future context while preserving session history.",
|
|
713
|
-
path: ["contextVirtualization"],
|
|
714
|
-
effectiveValue: core.contextVirtualization,
|
|
715
|
-
effectiveSource: sources.contextVirtualization,
|
|
716
|
-
defaultValue: DEFAULT_CONTEXT_VIRTUALIZATION_ENABLED,
|
|
717
|
-
});
|
|
718
|
-
const conversationHistoryItem = createScopedBooleanItem({
|
|
719
|
-
scope,
|
|
720
|
-
rawConfig,
|
|
721
|
-
localConfig,
|
|
722
|
-
id: "freeflow.conversationHistory",
|
|
723
|
-
label: "Conversation History",
|
|
724
|
-
description: "Let the model search and retrieve hidden conversation history on the active branch.",
|
|
725
|
-
path: ["conversationHistory"],
|
|
726
|
-
effectiveValue: core.conversationHistory,
|
|
727
|
-
effectiveSource: sources.conversationHistory,
|
|
728
|
-
defaultValue: DEFAULT_CONVERSATION_HISTORY_ENABLED,
|
|
729
|
-
});
|
|
730
807
|
const freeflowInactive = !core.enabled;
|
|
731
|
-
contextVirtualizationItem.inactive = freeflowInactive;
|
|
732
|
-
conversationHistoryItem.inactive = freeflowInactive;
|
|
733
808
|
const cognitiveRoutingState = options.cognitiveRouting;
|
|
734
809
|
const cognitiveRoutingRuntimeDisabled = options.runtimeAvailable !== true;
|
|
735
810
|
const cognitiveRoutingGroup = (() => {
|
|
@@ -814,26 +889,86 @@ function freeflowItems(rawConfig, options = {}) {
|
|
|
814
889
|
],
|
|
815
890
|
};
|
|
816
891
|
})();
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
892
|
+
const toolExecutionState = options.toolExecution;
|
|
893
|
+
const toolSource = (path) => {
|
|
894
|
+
if (getPath(localConfig, path) !== undefined) return "local";
|
|
895
|
+
if (getPath(rawConfig, path) !== undefined) return "repository";
|
|
896
|
+
return "builtin";
|
|
897
|
+
};
|
|
898
|
+
const toolExecutionEnabledItem = createScopedBooleanItem({
|
|
899
|
+
scope,
|
|
900
|
+
rawConfig,
|
|
901
|
+
localConfig,
|
|
902
|
+
id: "freeflow.toolExecution.enabled",
|
|
903
|
+
label: "Enabled",
|
|
904
|
+
description: "Enable the Tool Execution capability.",
|
|
905
|
+
path: ["toolExecution", "enabled"],
|
|
906
|
+
effectiveValue: toolExecutionState?.enabled ?? false,
|
|
907
|
+
effectiveSource: toolSource(["toolExecution", "enabled"]),
|
|
908
|
+
defaultValue: false,
|
|
909
|
+
});
|
|
910
|
+
const toolExecutionItems = [toolExecutionEnabledItem];
|
|
911
|
+
walkSettingsItems(toolExecutionItems, (item) => {
|
|
912
|
+
item.inactive = freeflowInactive;
|
|
913
|
+
});
|
|
914
|
+
const toolExecutionGroup = {
|
|
915
|
+
id: "freeflow.toolExecution",
|
|
916
|
+
label: "Tool Execution",
|
|
917
|
+
description: "Configure Freeflow's execution layer around Pi's tools.",
|
|
918
|
+
kind: "group",
|
|
919
|
+
value: toolExecutionState?.enabled ?? false,
|
|
920
|
+
inactive: freeflowInactive,
|
|
921
|
+
displaySuffix: toolExecutionState?.effective ? "active" : toolExecutionState?.enabled ? "inactive" : "disabled",
|
|
922
|
+
children: toolExecutionItems,
|
|
923
|
+
};
|
|
924
|
+
const compactionState = options.compaction;
|
|
925
|
+
const compactionEnabledItem = createScopedBooleanItem({
|
|
926
|
+
scope,
|
|
927
|
+
rawConfig,
|
|
928
|
+
localConfig,
|
|
929
|
+
id: "freeflow.compaction.enabled",
|
|
930
|
+
label: "Enabled",
|
|
931
|
+
description: "Warn before Pi's compaction limit and let the agent compact itself and recover in the same run.",
|
|
932
|
+
path: ["compaction", "enabled"],
|
|
933
|
+
effectiveValue: compactionState?.enabled ?? DEFAULT_COMPACTION_ENABLED,
|
|
934
|
+
effectiveSource: toolSource(["compaction", "enabled"]),
|
|
935
|
+
defaultValue: DEFAULT_COMPACTION_ENABLED,
|
|
936
|
+
});
|
|
937
|
+
const compactionCarryItem = createScopedBooleanItem({
|
|
938
|
+
scope,
|
|
939
|
+
rawConfig,
|
|
940
|
+
localConfig,
|
|
941
|
+
id: "freeflow.compaction.carry",
|
|
942
|
+
label: "Context reuse",
|
|
943
|
+
description: "Let the agent carry selected files and tool results into the next cycle.",
|
|
944
|
+
path: ["compaction", "carry"],
|
|
945
|
+
effectiveValue: compactionState?.carry ?? DEFAULT_COMPACTION_CARRY,
|
|
946
|
+
effectiveSource: toolSource(["compaction", "carry"]),
|
|
947
|
+
defaultValue: DEFAULT_COMPACTION_CARRY,
|
|
948
|
+
});
|
|
949
|
+
const compactionItems = [compactionEnabledItem, compactionCarryItem];
|
|
950
|
+
walkSettingsItems(compactionItems, (item) => {
|
|
951
|
+
item.inactive = freeflowInactive;
|
|
952
|
+
});
|
|
953
|
+
const compactionGroup = {
|
|
954
|
+
id: "freeflow.compaction",
|
|
955
|
+
label: "Compaction",
|
|
956
|
+
description: "Configure Freeflow compaction; Pi's own compaction stays as the fallback.",
|
|
957
|
+
kind: "group",
|
|
958
|
+
value: compactionState?.enabled ?? DEFAULT_COMPACTION_ENABLED,
|
|
959
|
+
inactive: freeflowInactive,
|
|
960
|
+
displaySuffix: compactionState?.effective ? "active" : compactionState?.enabled ? "inactive" : "disabled",
|
|
961
|
+
children: compactionItems,
|
|
962
|
+
};
|
|
963
|
+
return [freeflowItem, ...(cognitiveRoutingGroup ? [cognitiveRoutingGroup] : []), toolExecutionGroup, compactionGroup];
|
|
830
964
|
}
|
|
831
965
|
function pruneKnownDefaults(config) {
|
|
832
966
|
const defaultPaths = [
|
|
833
967
|
{ path: ["enabled"], value: DEFAULT_FREEFLOW_ENABLED },
|
|
834
|
-
{ path: ["contextVirtualization"], value: DEFAULT_CONTEXT_VIRTUALIZATION_ENABLED },
|
|
835
|
-
{ path: ["conversationHistory"], value: DEFAULT_CONVERSATION_HISTORY_ENABLED },
|
|
836
968
|
{ path: ["cognitiveRouting", "delegation"], value: "executor" },
|
|
969
|
+
{ path: ["toolExecution", "enabled"], value: DEFAULT_TOOL_EXECUTION_CONFIG.enabled },
|
|
970
|
+
{ path: ["compaction", "enabled"], value: DEFAULT_COMPACTION_ENABLED },
|
|
971
|
+
{ path: ["compaction", "carry"], value: DEFAULT_COMPACTION_CARRY },
|
|
837
972
|
];
|
|
838
973
|
for (const item of defaultPaths) {
|
|
839
974
|
if (valuesEqual(getPath(config, item.path), item.value)) {
|
|
@@ -909,25 +1044,34 @@ async function updateConfig(cwd, item, value, scope = item.configScope ?? "repos
|
|
|
909
1044
|
function isCognitiveRoutingProfileItem(item) {
|
|
910
1045
|
return ["coordinator", "helper", "executor"].some((profile) => item.id === `freeflow.cognitiveRouting.${profile}`);
|
|
911
1046
|
}
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
if (isCognitiveRoutingProfileItem(item)
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
value
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
1047
|
+
/** A value as the settings screen shows it, e.g. "enabled · personal" or "inherit → enabled (repository)". */
|
|
1048
|
+
function formatSettingValue(item, value) {
|
|
1049
|
+
if (isCognitiveRoutingProfileItem(item)) return cognitiveRoutingProfileDisplay(value);
|
|
1050
|
+
if (typeof value === "boolean") return booleanValue(value);
|
|
1051
|
+
const key = String(value ?? "");
|
|
1052
|
+
return item.valueLabels?.[key] && key !== LOCAL_INHERIT ? item.valueLabels[key] : formatCoreValue(value);
|
|
1053
|
+
}
|
|
1054
|
+
function displayValue(item) {
|
|
1055
|
+
if (item.kind === "group") return item.displaySuffix ?? "";
|
|
1056
|
+
if (item.transient && item.format) return item.format(item.value);
|
|
1057
|
+
if (item.id === "freeflow.cognitiveRouting.profile")
|
|
1058
|
+
return item.valueLabels?.[String(item.value)] ?? String(item.value);
|
|
1059
|
+
if (item.configScope === "local" || item.configScope === "session") {
|
|
1060
|
+
const inherits = item.value === LOCAL_INHERIT || item.value === undefined;
|
|
1061
|
+
if (inherits) {
|
|
1062
|
+
if (item.inheritedValue === undefined) return "not configured";
|
|
1063
|
+
return `inherit → ${formatSettingValue(item, item.inheritedValue)} (${SOURCE_LABELS[item.inheritedSource ?? "builtin"]})`;
|
|
1064
|
+
}
|
|
1065
|
+
return `${formatSettingValue(item, effectiveItemValue(item))} · ${SOURCE_LABELS[item.configScope]}`;
|
|
928
1066
|
}
|
|
929
|
-
|
|
1067
|
+
const value = item.value === undefined && isCognitiveRoutingProfileItem(item) ? undefined : item.value;
|
|
1068
|
+
const shown = formatSettingValue(item, value);
|
|
1069
|
+
if (item.localOverrideValue !== undefined && !valuesEqual(item.localOverrideValue, item.value))
|
|
1070
|
+
return `${shown} · personal override: ${formatSettingValue(item, item.localOverrideValue)}`;
|
|
1071
|
+
return item.effectiveSource === "builtin" && item.defaultValue !== undefined ? `${shown} · default` : shown;
|
|
930
1072
|
}
|
|
1073
|
+
/** Small choices change in place; holding a profile switches models and a reset clears everything, so those ask. */
|
|
1074
|
+
const PICKED_CHOICES = new Set(["freeflow.cognitiveRouting.profile", "freeflow.session.reset"]);
|
|
931
1075
|
function walkSettingsItems(items, visitor) {
|
|
932
1076
|
for (const item of items) {
|
|
933
1077
|
visitor(item);
|
|
@@ -973,6 +1117,24 @@ function refreshSettingsDerivedState(items) {
|
|
|
973
1117
|
contextGroup.value = enabledCount > 0;
|
|
974
1118
|
contextGroup.displaySuffix = `${enabledCount}/${contextGroup.children.length} enabled`;
|
|
975
1119
|
}
|
|
1120
|
+
const toolExecutionGroup = findSettingsItem(items, "freeflow.toolExecution");
|
|
1121
|
+
const toolExecutionEnabled = findSettingsItem(items, "freeflow.toolExecution.enabled");
|
|
1122
|
+
if (toolExecutionGroup && toolExecutionEnabled) {
|
|
1123
|
+
const enabled = !freeflowInactive && effectiveItemValue(toolExecutionEnabled) === true;
|
|
1124
|
+
toolExecutionGroup.value = effectiveItemValue(toolExecutionEnabled) === true;
|
|
1125
|
+
toolExecutionGroup.displaySuffix = enabled
|
|
1126
|
+
? "active"
|
|
1127
|
+
: effectiveItemValue(toolExecutionEnabled) === true
|
|
1128
|
+
? "inactive"
|
|
1129
|
+
: "disabled";
|
|
1130
|
+
}
|
|
1131
|
+
const compactionGroup = findSettingsItem(items, "freeflow.compaction");
|
|
1132
|
+
const compactionEnabled = findSettingsItem(items, "freeflow.compaction.enabled");
|
|
1133
|
+
if (compactionGroup && compactionEnabled) {
|
|
1134
|
+
const on = effectiveItemValue(compactionEnabled) === true;
|
|
1135
|
+
compactionGroup.value = on;
|
|
1136
|
+
compactionGroup.displaySuffix = on && !freeflowInactive ? "active" : on ? "inactive" : "disabled";
|
|
1137
|
+
}
|
|
976
1138
|
walkSettingsItems(items, (candidate) => {
|
|
977
1139
|
if (candidate.id === "freeflow.session.reset") {
|
|
978
1140
|
candidate.inactive = false;
|
|
@@ -989,8 +1151,8 @@ function refreshSettingsDerivedState(items) {
|
|
|
989
1151
|
function settingsChoices(item) {
|
|
990
1152
|
if (item.kind === "boolean") {
|
|
991
1153
|
return [
|
|
992
|
-
{ key: "true", value: true, label: "enabled"
|
|
993
|
-
{ key: "false", value: false, label: "disabled"
|
|
1154
|
+
{ key: "true", value: true, label: "enabled" },
|
|
1155
|
+
{ key: "false", value: false, label: "disabled" },
|
|
994
1156
|
];
|
|
995
1157
|
}
|
|
996
1158
|
if (item.kind !== "enum") return undefined;
|
|
@@ -1013,41 +1175,51 @@ function resetSessionItemState(items) {
|
|
|
1013
1175
|
});
|
|
1014
1176
|
}
|
|
1015
1177
|
function settingsEntries(items, rootItems, onChange) {
|
|
1016
|
-
return items.
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
:
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
:
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
item.
|
|
1178
|
+
return items.flatMap((item) => {
|
|
1179
|
+
if (item.kind === "group" && item.children) {
|
|
1180
|
+
const section = {
|
|
1181
|
+
id: item.id,
|
|
1182
|
+
label: item.label,
|
|
1183
|
+
description: item.description,
|
|
1184
|
+
section: true,
|
|
1185
|
+
currentValue: () => displayValue(item),
|
|
1186
|
+
inactive: () => item.inactive === true,
|
|
1187
|
+
};
|
|
1188
|
+
return [section, ...settingsEntries(item.children, rootItems, onChange)];
|
|
1189
|
+
}
|
|
1190
|
+
const choices = settingsChoices(item);
|
|
1191
|
+
return [
|
|
1192
|
+
{
|
|
1193
|
+
id: item.id,
|
|
1194
|
+
label: item.label,
|
|
1195
|
+
description: item.description,
|
|
1196
|
+
currentValue: () => displayValue(item),
|
|
1197
|
+
inactive: () => item.inactive === true,
|
|
1198
|
+
currentChoiceKey: () => String(item.value),
|
|
1199
|
+
choices,
|
|
1200
|
+
cycle: Boolean(choices?.length) && !PICKED_CHOICES.has(item.id),
|
|
1201
|
+
wizard: item.wizard ? () => item.wizard() : undefined,
|
|
1202
|
+
edit:
|
|
1203
|
+
!["boolean", "enum", "group"].includes(item.kind) && !item.wizard
|
|
1204
|
+
? {
|
|
1205
|
+
initialValue: () => item.editInitialValue?.() ?? displayValue(item),
|
|
1206
|
+
parse: (text) => (item.parse ? item.parse(text) : text),
|
|
1045
1207
|
}
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1208
|
+
: undefined,
|
|
1209
|
+
commit: async (value) => {
|
|
1210
|
+
if (valuesEqual(item.value, value)) return { changed: false, reloadRequired: false };
|
|
1211
|
+
const outcome = await onChange(item, value);
|
|
1212
|
+
if (outcome.changed) {
|
|
1213
|
+
updateScopedItemState(item, value);
|
|
1214
|
+
if (item.id === "freeflow.session.reset") resetSessionItemState(rootItems);
|
|
1215
|
+
else if (!item.transient) item.value = value;
|
|
1216
|
+
refreshSettingsDerivedState(rootItems);
|
|
1217
|
+
}
|
|
1218
|
+
return outcome;
|
|
1219
|
+
},
|
|
1220
|
+
},
|
|
1221
|
+
];
|
|
1222
|
+
});
|
|
1051
1223
|
}
|
|
1052
1224
|
async function openSettings(options) {
|
|
1053
1225
|
if (typeof options.ctx?.ui?.custom !== "function") {
|
|
@@ -1057,16 +1229,12 @@ async function openSettings(options) {
|
|
|
1057
1229
|
);
|
|
1058
1230
|
return { changed: false, configChanged: false, failed: false };
|
|
1059
1231
|
}
|
|
1060
|
-
const entries = settingsEntries(options.items, options.items, options.onChange);
|
|
1061
|
-
const initialChoice = options.initialChoice
|
|
1062
|
-
? entries.find((entry) => entry.id === options.initialChoice?.id)
|
|
1063
|
-
: undefined;
|
|
1064
1232
|
let component;
|
|
1065
1233
|
await options.ctx.ui.custom((tui, theme, _keybindings, done) => {
|
|
1066
1234
|
component = new PiSettingsComponent({
|
|
1067
1235
|
title: options.title,
|
|
1068
|
-
|
|
1069
|
-
|
|
1236
|
+
scopes: options.scopes,
|
|
1237
|
+
initialScope: options.initialScope,
|
|
1070
1238
|
theme: theme ?? {},
|
|
1071
1239
|
requestRender: () => tui.requestRender(),
|
|
1072
1240
|
notify: (message, level) => options.ctx?.ui?.notify?.(message, level),
|
|
@@ -1077,33 +1245,24 @@ async function openSettings(options) {
|
|
|
1077
1245
|
await component?.waitForWrites();
|
|
1078
1246
|
return component?.sessionResult() ?? { changed: false, configChanged: false, failed: false };
|
|
1079
1247
|
}
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
|
|
1089
|
-
|
|
1090
|
-
|
|
1091
|
-
|
|
1092
|
-
|
|
1093
|
-
|
|
1094
|
-
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
|
|
1098
|
-
: "disabled"
|
|
1099
|
-
: undefined;
|
|
1100
|
-
const contextEnabled = state.contextVirtualization?.effective || state.conversationHistory?.effective;
|
|
1101
|
-
return [
|
|
1102
|
-
`Freeflow: ${state.enabled ? "enabled" : "disabled"}${sessionSuffix(state.configSources.enabled)}`,
|
|
1103
|
-
`context: ${contextEnabled ? "enabled" : "disabled"} (virtualization ${state.contextVirtualization?.effective ? "enabled" : "disabled"}, history ${state.conversationHistory?.effective ? "enabled" : "disabled"})`,
|
|
1104
|
-
...(cognitiveRoutingStatus ? [`cognitive routing: ${cognitiveRoutingStatus}`] : []),
|
|
1105
|
-
].join("; ");
|
|
1106
|
-
}
|
|
1248
|
+
/** Where each scope's values live and whom they affect, shown under the tabs. */
|
|
1249
|
+
const SCOPES = [
|
|
1250
|
+
{
|
|
1251
|
+
id: "session",
|
|
1252
|
+
label: "Session",
|
|
1253
|
+
summary: "This Pi session only, not saved to a file. Overrides personal and repository values.",
|
|
1254
|
+
},
|
|
1255
|
+
{
|
|
1256
|
+
id: "local",
|
|
1257
|
+
label: "Personal",
|
|
1258
|
+
summary: ".freeflow/local.json: only you, in this checkout. Overrides the repository.",
|
|
1259
|
+
},
|
|
1260
|
+
{
|
|
1261
|
+
id: "repository",
|
|
1262
|
+
label: "Repository",
|
|
1263
|
+
summary: ".freeflow/config.json: shared with everyone who uses this repository.",
|
|
1264
|
+
},
|
|
1265
|
+
];
|
|
1107
1266
|
const NON_TUI_SETTINGS_GUIDANCE =
|
|
1108
1267
|
"Freeflow settings require Pi TUI mode. Use /freeflow status to inspect current state; supported non-TUI changes are /freeflow enable and /freeflow disable.";
|
|
1109
1268
|
function ensureSettingsIdle(ctx) {
|
|
@@ -1151,7 +1310,7 @@ async function finalizeSessionSettings(session, ctx, afterChange) {
|
|
|
1151
1310
|
await ctx.reload();
|
|
1152
1311
|
return true;
|
|
1153
1312
|
}
|
|
1154
|
-
export async function handleFreeflowCommand(args, ctx, afterChange, pi, cognitiveRoutingController) {
|
|
1313
|
+
export async function handleFreeflowCommand(args, ctx, afterChange, pi, cognitiveRoutingController, diagnostics) {
|
|
1155
1314
|
const input = (args ?? "settings").trim().toLowerCase() || "settings";
|
|
1156
1315
|
const [action, ...rest] = input.split(/\s+/);
|
|
1157
1316
|
const actionValue = rest.join(" ");
|
|
@@ -1162,14 +1321,11 @@ export async function handleFreeflowCommand(args, ctx, afterChange, pi, cognitiv
|
|
|
1162
1321
|
if (nonTui && settingsSelector) {
|
|
1163
1322
|
return nonTuiGuidance(ctx, NON_TUI_SETTINGS_GUIDANCE);
|
|
1164
1323
|
}
|
|
1165
|
-
const [layers, state] = await Promise.all([
|
|
1166
|
-
readFreeflowConfigLayers(ctx.cwd),
|
|
1167
|
-
readCapabilityState(ctx.cwd, ctx, pi?.host),
|
|
1168
|
-
]);
|
|
1324
|
+
const [layers, state] = await Promise.all([readFreeflowConfigLayers(ctx.cwd), readCapabilityState(ctx.cwd, ctx)]);
|
|
1169
1325
|
const configState = layers.repository;
|
|
1170
1326
|
const raw = configState.valid ? configState.parsed : {};
|
|
1171
1327
|
if (action === "status") {
|
|
1172
|
-
ctx.ui.notify(freeflowStatusText(state, cognitiveRoutingController), "info");
|
|
1328
|
+
ctx.ui.notify(freeflowStatusText(state, cognitiveRoutingController, diagnostics?.status()), "info");
|
|
1173
1329
|
return { changed: false, reloaded: false };
|
|
1174
1330
|
}
|
|
1175
1331
|
if (!configState.valid) {
|
|
@@ -1190,6 +1346,8 @@ export async function handleFreeflowCommand(args, ctx, afterChange, pi, cognitiv
|
|
|
1190
1346
|
scope: "repository",
|
|
1191
1347
|
layers,
|
|
1192
1348
|
cognitiveRouting: state.cognitiveRouting,
|
|
1349
|
+
toolExecution: state.toolExecution,
|
|
1350
|
+
compaction: state.compaction,
|
|
1193
1351
|
ctx,
|
|
1194
1352
|
runtimeAvailable: isCognitiveRoutingRuntimeAvailable(pi),
|
|
1195
1353
|
}).find((candidate) => candidate.id === "freeflow.enabled");
|
|
@@ -1210,11 +1368,11 @@ export async function handleFreeflowCommand(args, ctx, afterChange, pi, cognitiv
|
|
|
1210
1368
|
);
|
|
1211
1369
|
return { changed: false, reloaded: false, error: "invalid_action" };
|
|
1212
1370
|
}
|
|
1213
|
-
let
|
|
1371
|
+
let initialScope = "local";
|
|
1214
1372
|
if (actionValue === "session") {
|
|
1215
|
-
|
|
1373
|
+
initialScope = "session";
|
|
1216
1374
|
} else if (["repo", "repository", "shared"].includes(actionValue)) {
|
|
1217
|
-
|
|
1375
|
+
initialScope = "repository";
|
|
1218
1376
|
} else if (actionValue && !["local", "personal"].includes(actionValue)) {
|
|
1219
1377
|
ctx.ui.notify(
|
|
1220
1378
|
"Usage: /freeflow settings, /freeflow settings session, /freeflow settings local, or /freeflow settings repo",
|
|
@@ -1223,138 +1381,190 @@ export async function handleFreeflowCommand(args, ctx, afterChange, pi, cognitiv
|
|
|
1223
1381
|
return { changed: false, reloaded: false, error: "invalid_scope" };
|
|
1224
1382
|
}
|
|
1225
1383
|
if (!ensureSettingsIdle(ctx)) return { changed: false, reloaded: false, error: "busy" };
|
|
1226
|
-
|
|
1227
|
-
const
|
|
1228
|
-
|
|
1229
|
-
|
|
1230
|
-
|
|
1231
|
-
|
|
1232
|
-
|
|
1233
|
-
|
|
1234
|
-
|
|
1235
|
-
|
|
1236
|
-
|
|
1237
|
-
|
|
1238
|
-
|
|
1239
|
-
|
|
1240
|
-
|
|
1241
|
-
|
|
1242
|
-
|
|
1243
|
-
|
|
1244
|
-
|
|
1245
|
-
|
|
1246
|
-
|
|
1247
|
-
|
|
1248
|
-
|
|
1249
|
-
|
|
1384
|
+
// Each scope is read fresh when shown, so a change in one scope shows up in the others' inherited values.
|
|
1385
|
+
const touched = new Set();
|
|
1386
|
+
const loadItems = async (scope) => {
|
|
1387
|
+
const [layersNow, stateNow] = await Promise.all([
|
|
1388
|
+
readFreeflowConfigLayers(ctx.cwd),
|
|
1389
|
+
readCapabilityState(ctx.cwd, ctx),
|
|
1390
|
+
]);
|
|
1391
|
+
const rawNow = layersNow.repository.valid ? layersNow.repository.parsed : {};
|
|
1392
|
+
if (scope === "session") {
|
|
1393
|
+
const localNow = layersNow.local.valid && isRecord(layersNow.local.parsed) ? layersNow.local.parsed : {};
|
|
1394
|
+
return sessionFreeflowItems(stateNow, cognitiveRoutingController, ctx, rawNow, localNow);
|
|
1395
|
+
}
|
|
1396
|
+
return freeflowItems(rawNow, {
|
|
1397
|
+
scope,
|
|
1398
|
+
layers: layersNow,
|
|
1399
|
+
cognitiveRouting: stateNow.cognitiveRouting,
|
|
1400
|
+
toolExecution: stateNow.toolExecution,
|
|
1401
|
+
compaction: stateNow.compaction,
|
|
1402
|
+
ctx,
|
|
1403
|
+
runtimeAvailable: isCognitiveRoutingRuntimeAvailable(pi),
|
|
1404
|
+
});
|
|
1405
|
+
};
|
|
1406
|
+
const scopes = SCOPES.map((scope) => ({
|
|
1407
|
+
...scope,
|
|
1408
|
+
load: async () => {
|
|
1409
|
+
const items = await loadItems(scope.id);
|
|
1410
|
+
return settingsEntries(items, items, async (item, value) => {
|
|
1411
|
+
const outcome = await changeSetting(scope.id, item, value);
|
|
1412
|
+
if (outcome.changed) touched.add(scope.id);
|
|
1413
|
+
return outcome;
|
|
1414
|
+
});
|
|
1415
|
+
},
|
|
1416
|
+
}));
|
|
1417
|
+
const changeSetting = async (settingsScope, item, value) => {
|
|
1418
|
+
if (!ensureSettingsIdle(ctx)) return { changed: false, reloadRequired: false };
|
|
1419
|
+
if (!isCognitiveRoutingRuntimeAvailable(pi) && item.id.startsWith("freeflow.cognitiveRouting.")) {
|
|
1420
|
+
ctx.ui.notify("Cognitive Routing is unavailable because this host lacks model-state controls.", "warning");
|
|
1421
|
+
return { changed: false, reloadRequired: false };
|
|
1422
|
+
}
|
|
1423
|
+
if (item.id === "freeflow.cognitiveRouting.profile") {
|
|
1424
|
+
if (!cognitiveRoutingController) {
|
|
1425
|
+
ctx.ui.notify("Cognitive Routing is unavailable for this session.", "warning");
|
|
1250
1426
|
return { changed: false, reloadRequired: false };
|
|
1251
1427
|
}
|
|
1252
|
-
|
|
1253
|
-
|
|
1254
|
-
|
|
1255
|
-
|
|
1256
|
-
|
|
1257
|
-
|
|
1258
|
-
|
|
1259
|
-
? await cognitiveRoutingController.setAutomaticControl("profile-settings")
|
|
1260
|
-
: await cognitiveRoutingController.setManualProfile(value, "profile-settings");
|
|
1261
|
-
if ((result.status !== "automatic" && result.status !== "active") || result.reason) {
|
|
1262
|
-
ctx.ui.notify(
|
|
1263
|
-
`Cognitive Routing settings could not be applied: ${result.reason ?? result.status}.`,
|
|
1264
|
-
"warning",
|
|
1265
|
-
);
|
|
1266
|
-
return { changed: false, reloadRequired: false };
|
|
1267
|
-
}
|
|
1268
|
-
await afterChange(false);
|
|
1269
|
-
return { changed: true, reloadRequired: false };
|
|
1428
|
+
const result =
|
|
1429
|
+
value === "auto"
|
|
1430
|
+
? await cognitiveRoutingController.setAutomaticControl("profile-settings")
|
|
1431
|
+
: await cognitiveRoutingController.setManualProfile(value, "profile-settings");
|
|
1432
|
+
if ((result.status !== "automatic" && result.status !== "active") || result.reason) {
|
|
1433
|
+
ctx.ui.notify(`Cognitive Routing settings could not be applied: ${result.reason ?? result.status}.`, "warning");
|
|
1434
|
+
return { changed: false, reloadRequired: false };
|
|
1270
1435
|
}
|
|
1271
|
-
|
|
1272
|
-
|
|
1273
|
-
|
|
1274
|
-
|
|
1275
|
-
|
|
1276
|
-
|
|
1277
|
-
|
|
1278
|
-
|
|
1279
|
-
|
|
1280
|
-
|
|
1281
|
-
|
|
1282
|
-
|
|
1283
|
-
|
|
1284
|
-
|
|
1285
|
-
|
|
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
|
-
}
|
|
1436
|
+
await afterChange(false);
|
|
1437
|
+
return { changed: true, reloadRequired: false };
|
|
1438
|
+
}
|
|
1439
|
+
if (settingsScope === "session" && item.id === "freeflow.cognitiveRouting.delegation") {
|
|
1440
|
+
if (!cognitiveRoutingController) return { changed: false, reloadRequired: false };
|
|
1441
|
+
const result = await cognitiveRoutingController.setSessionDelegationOverride(
|
|
1442
|
+
value === LOCAL_INHERIT ? null : value,
|
|
1443
|
+
"Session delegation settings",
|
|
1444
|
+
);
|
|
1445
|
+
if (result.status === "blocked" || result.reason) {
|
|
1446
|
+
ctx.ui.notify(
|
|
1447
|
+
`Cognitive Routing delegation mode could not be applied: ${result.reason ?? result.status}.`,
|
|
1448
|
+
"warning",
|
|
1449
|
+
);
|
|
1450
|
+
return { changed: false, reloadRequired: false };
|
|
1298
1451
|
}
|
|
1299
|
-
|
|
1300
|
-
|
|
1301
|
-
|
|
1302
|
-
|
|
1303
|
-
|
|
1304
|
-
|
|
1305
|
-
|
|
1306
|
-
|
|
1307
|
-
|
|
1308
|
-
|
|
1309
|
-
|
|
1310
|
-
|
|
1311
|
-
thinking: value.thinking,
|
|
1312
|
-
};
|
|
1313
|
-
const result = await cognitiveRoutingController.setSessionProfileOverride(
|
|
1314
|
-
profile,
|
|
1315
|
-
override,
|
|
1316
|
-
"Session profile settings",
|
|
1452
|
+
await afterChange(false);
|
|
1453
|
+
return { changed: result.status !== "unchanged", reloadRequired: false };
|
|
1454
|
+
}
|
|
1455
|
+
if (item.id === "freeflow.session.reset") {
|
|
1456
|
+
const previousDelegation = cognitiveRoutingController?.sessionDelegationOverride() ?? null;
|
|
1457
|
+
const delegationResult = cognitiveRoutingController
|
|
1458
|
+
? await cognitiveRoutingController.setSessionDelegationOverride(null, "Reset session delegation mode")
|
|
1459
|
+
: { status: "unchanged" };
|
|
1460
|
+
if (delegationResult.status === "blocked" || delegationResult.reason) {
|
|
1461
|
+
ctx.ui.notify(
|
|
1462
|
+
`Cognitive Routing session mode could not be reset: ${delegationResult.reason ?? delegationResult.status}.`,
|
|
1463
|
+
"warning",
|
|
1317
1464
|
);
|
|
1318
|
-
|
|
1319
|
-
|
|
1320
|
-
|
|
1321
|
-
|
|
1465
|
+
return { changed: false, reloadRequired: false };
|
|
1466
|
+
}
|
|
1467
|
+
const routingResult = cognitiveRoutingController
|
|
1468
|
+
? await cognitiveRoutingController.resetSessionProfileOverrides("Reset session overrides")
|
|
1469
|
+
: { status: "unchanged" };
|
|
1470
|
+
if (routingResult.status === "blocked" || routingResult.reason) {
|
|
1471
|
+
if (delegationResult.status !== "unchanged") {
|
|
1472
|
+
const restored = await cognitiveRoutingController.setSessionDelegationOverride(
|
|
1473
|
+
previousDelegation,
|
|
1474
|
+
"Session reset failed; prior delegation mode retained",
|
|
1322
1475
|
);
|
|
1323
|
-
|
|
1476
|
+
if (restored.status === "blocked" || restored.reason) {
|
|
1477
|
+
await afterChange(false);
|
|
1478
|
+
ctx.ui.notify(
|
|
1479
|
+
`Session mode was reset but could not be restored: ${restored.reason ?? restored.status}. Reopen settings to see the current mode.`,
|
|
1480
|
+
"warning",
|
|
1481
|
+
);
|
|
1482
|
+
}
|
|
1324
1483
|
}
|
|
1325
|
-
|
|
1326
|
-
|
|
1484
|
+
ctx.ui.notify(
|
|
1485
|
+
`Cognitive Routing session presets could not be reset: ${routingResult.reason ?? routingResult.status}.`,
|
|
1486
|
+
"warning",
|
|
1487
|
+
);
|
|
1488
|
+
return { changed: false, reloadRequired: false };
|
|
1327
1489
|
}
|
|
1328
|
-
|
|
1329
|
-
const
|
|
1330
|
-
|
|
1331
|
-
|
|
1332
|
-
|
|
1490
|
+
try {
|
|
1491
|
+
const result = await resetSessionOverrides(ctx, pi);
|
|
1492
|
+
return {
|
|
1493
|
+
changed: result.changed || routingResult.status !== "unchanged" || delegationResult.status !== "unchanged",
|
|
1494
|
+
reloadRequired: result.reloadRequired,
|
|
1495
|
+
};
|
|
1496
|
+
} catch (error) {
|
|
1497
|
+
ctx.ui.notify(
|
|
1498
|
+
`Routing presets were reset, but core/context session overrides could not be reset: ${error instanceof Error ? error.message : String(error)}`,
|
|
1499
|
+
"warning",
|
|
1500
|
+
);
|
|
1501
|
+
return {
|
|
1502
|
+
changed: routingResult.status !== "unchanged" || delegationResult.status !== "unchanged",
|
|
1503
|
+
reloadRequired: false,
|
|
1333
1504
|
};
|
|
1334
|
-
const key = keyById[item.id];
|
|
1335
|
-
const override = value === LOCAL_INHERIT ? null : value === "true";
|
|
1336
|
-
const result = await setSessionCoreOverride(key, override, ctx, pi);
|
|
1337
|
-
return { changed: result.changed, reloadRequired: result.reloadRequired === true };
|
|
1338
1505
|
}
|
|
1339
|
-
|
|
1340
|
-
|
|
1341
|
-
|
|
1506
|
+
}
|
|
1507
|
+
if (item.configScope === "session" && isCognitiveRoutingProfileItem(item)) {
|
|
1508
|
+
if (!cognitiveRoutingController) {
|
|
1509
|
+
ctx.ui.notify("Cognitive Routing is unavailable for this session.", "warning");
|
|
1510
|
+
return { changed: false, reloadRequired: false };
|
|
1342
1511
|
}
|
|
1343
|
-
|
|
1344
|
-
|
|
1345
|
-
|
|
1346
|
-
|
|
1347
|
-
|
|
1512
|
+
const profile = item.id.split(".").at(-1);
|
|
1513
|
+
const override =
|
|
1514
|
+
value === undefined || value === LOCAL_INHERIT
|
|
1515
|
+
? null
|
|
1516
|
+
: {
|
|
1517
|
+
provider: value.provider,
|
|
1518
|
+
modelId: value.model,
|
|
1519
|
+
thinking: value.thinking,
|
|
1520
|
+
};
|
|
1521
|
+
const result = await cognitiveRoutingController.setSessionProfileOverride(
|
|
1522
|
+
profile,
|
|
1523
|
+
override,
|
|
1524
|
+
"Session profile settings",
|
|
1525
|
+
);
|
|
1526
|
+
if (result.status === "blocked" || result.reason) {
|
|
1527
|
+
ctx.ui.notify(
|
|
1528
|
+
`Cognitive Routing session preset could not be applied: ${result.reason ?? result.status}.`,
|
|
1529
|
+
"warning",
|
|
1530
|
+
);
|
|
1531
|
+
return { changed: false, reloadRequired: false };
|
|
1532
|
+
}
|
|
1533
|
+
await afterChange(false);
|
|
1534
|
+
return { changed: result.status !== "unchanged", reloadRequired: false };
|
|
1535
|
+
}
|
|
1536
|
+
if (item.configScope === "session") {
|
|
1537
|
+
const key = item.path.join(".");
|
|
1538
|
+
const override = value === LOCAL_INHERIT ? null : value === "true";
|
|
1539
|
+
const result = await setSessionCoreOverride(key, override, ctx, pi);
|
|
1540
|
+
return { changed: result.changed, reloadRequired: result.reloadRequired === true };
|
|
1541
|
+
}
|
|
1542
|
+
await updateConfig(ctx.cwd, item, value, item.configScope ?? "repository");
|
|
1543
|
+
return { changed: true, reloadRequired: true };
|
|
1544
|
+
};
|
|
1545
|
+
// The first scope opens loaded; the others load when Tab reaches them.
|
|
1546
|
+
const first = scopes.find((scope) => scope.id === initialScope);
|
|
1547
|
+
const firstEntries = await first.load();
|
|
1548
|
+
let preloaded = true;
|
|
1549
|
+
const firstLoad = first.load;
|
|
1550
|
+
first.load = () => {
|
|
1551
|
+
if (!preloaded) return firstLoad();
|
|
1552
|
+
preloaded = false;
|
|
1553
|
+
return firstEntries;
|
|
1554
|
+
};
|
|
1555
|
+
const session = await openSettings({ title: "Freeflow settings", scopes, initialScope, ctx });
|
|
1556
|
+
// A file write reloads Freeflow; session-only changes reload only when they switch a capability.
|
|
1557
|
+
if (touched.has("local") || touched.has("repository")) {
|
|
1558
|
+
const reloaded = await finalizeSettingsSession(
|
|
1559
|
+
session,
|
|
1560
|
+
ctx,
|
|
1561
|
+
afterChange,
|
|
1562
|
+
"Freeflow settings saved. Reloading Freeflow runtime...",
|
|
1563
|
+
"Run /reload for Freeflow changes to fully apply.",
|
|
1564
|
+
{ reconcileCognitiveRouting: true },
|
|
1565
|
+
);
|
|
1348
1566
|
return { changed: session.changed, reloaded, error: session.failed ? "write_failed" : undefined };
|
|
1349
1567
|
}
|
|
1350
|
-
const
|
|
1351
|
-
const reloaded = await finalizeSettingsSession(
|
|
1352
|
-
session,
|
|
1353
|
-
ctx,
|
|
1354
|
-
afterChange,
|
|
1355
|
-
`Freeflow ${savedTarget} saved. Reloading Freeflow runtime...`,
|
|
1356
|
-
"Run /reload for Freeflow changes to fully apply.",
|
|
1357
|
-
{ reconcileCognitiveRouting },
|
|
1358
|
-
);
|
|
1568
|
+
const reloaded = await finalizeSessionSettings(session, ctx, afterChange);
|
|
1359
1569
|
return { changed: session.changed, reloaded, error: session.failed ? "write_failed" : undefined };
|
|
1360
1570
|
}
|